@wildo-ai/presets-components 1.1.5 → 1.1.6

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 (189) hide show
  1. package/dist/esm/.native.tsbuildinfo +1 -0
  2. package/dist/esm/components/actions/action-bar.d.ts.map +1 -1
  3. package/dist/esm/components/actions/action-bar.js +5 -2
  4. package/dist/esm/components/actions/action-bar.js.map +1 -1
  5. package/dist/esm/components/ai-elements/chat/suggestion.d.ts.map +1 -1
  6. package/dist/esm/components/ai-elements/chat/suggestion.js +8 -1
  7. package/dist/esm/components/ai-elements/chat/suggestion.js.map +1 -1
  8. package/dist/esm/components/ai-elements/decision/approval.d.ts +108 -13
  9. package/dist/esm/components/ai-elements/decision/approval.d.ts.map +1 -1
  10. package/dist/esm/components/ai-elements/decision/approval.js +60 -10
  11. package/dist/esm/components/ai-elements/decision/approval.js.map +1 -1
  12. package/dist/esm/components/ai-elements/graph/graph-runtime.d.ts +3 -1
  13. package/dist/esm/components/ai-elements/graph/graph-runtime.d.ts.map +1 -1
  14. package/dist/esm/components/ai-elements/graph/graph-runtime.js +3 -1
  15. package/dist/esm/components/ai-elements/graph/graph-runtime.js.map +1 -1
  16. package/dist/esm/components/app/app-layout-main-container.d.ts +9 -0
  17. package/dist/esm/components/app/app-layout-main-container.d.ts.map +1 -1
  18. package/dist/esm/components/app/app-layout-main-container.js +17 -23
  19. package/dist/esm/components/app/app-layout-main-container.js.map +1 -1
  20. package/dist/esm/components/app/app-layout-main-container.labels.d.ts.map +1 -1
  21. package/dist/esm/components/app/app-layout-main-container.labels.js +27 -0
  22. package/dist/esm/components/app/app-layout-main-container.labels.js.map +1 -1
  23. package/dist/esm/components/billing/billing-plan-change-preview.d.ts.map +1 -1
  24. package/dist/esm/components/billing/billing-plan-change-preview.js +5 -2
  25. package/dist/esm/components/billing/billing-plan-change-preview.js.map +1 -1
  26. package/dist/esm/components/billing/billing-plan-change-preview.labels.d.ts.map +1 -1
  27. package/dist/esm/components/billing/billing-plan-change-preview.labels.js +3 -0
  28. package/dist/esm/components/billing/billing-plan-change-preview.labels.js.map +1 -1
  29. package/dist/esm/components/billing/billing-pricing-table.d.ts +1 -1
  30. package/dist/esm/components/billing/billing-pricing-table.d.ts.map +1 -1
  31. package/dist/esm/components/billing/billing-pricing-table.js +8 -4
  32. package/dist/esm/components/billing/billing-pricing-table.js.map +1 -1
  33. package/dist/esm/components/billing/billing-pricing-table.labels.d.ts.map +1 -1
  34. package/dist/esm/components/billing/billing-pricing-table.labels.js +7 -1
  35. package/dist/esm/components/billing/billing-pricing-table.labels.js.map +1 -1
  36. package/dist/esm/components/billing/billing-usage-dashboard.d.ts.map +1 -1
  37. package/dist/esm/components/billing/billing-usage-dashboard.js +6 -3
  38. package/dist/esm/components/billing/billing-usage-dashboard.js.map +1 -1
  39. package/dist/esm/components/billing/billing-usage-dashboard.labels.d.ts.map +1 -1
  40. package/dist/esm/components/billing/billing-usage-dashboard.labels.js +2 -1
  41. package/dist/esm/components/billing/billing-usage-dashboard.labels.js.map +1 -1
  42. package/dist/esm/components/data-display/formatters/display-field-enum.d.ts.map +1 -1
  43. package/dist/esm/components/data-display/formatters/display-field-enum.js +18 -4
  44. package/dist/esm/components/data-display/formatters/display-field-enum.js.map +1 -1
  45. package/dist/esm/components/data-display/formatters/display-field-file.d.ts.map +1 -1
  46. package/dist/esm/components/data-display/formatters/display-field-file.js +13 -14
  47. package/dist/esm/components/data-display/formatters/display-field-file.js.map +1 -1
  48. package/dist/esm/components/data-display/formatters/display-field-file.labels.d.ts.map +1 -1
  49. package/dist/esm/components/data-display/formatters/display-field-file.labels.js +1 -0
  50. package/dist/esm/components/data-display/formatters/display-field-file.labels.js.map +1 -1
  51. package/dist/esm/components/data-display/formatters/display-field-number.d.ts.map +1 -1
  52. package/dist/esm/components/data-display/formatters/display-field-number.js +6 -1
  53. package/dist/esm/components/data-display/formatters/display-field-number.js.map +1 -1
  54. package/dist/esm/components/data-display/formatters/display-field-number.labels.d.ts.map +1 -1
  55. package/dist/esm/components/data-display/formatters/display-field-number.labels.js +5 -2
  56. package/dist/esm/components/data-display/formatters/display-field-number.labels.js.map +1 -1
  57. package/dist/esm/components/data-display/formatters/display-field-string.d.ts +1 -1
  58. package/dist/esm/components/data-display/formatters/display-field-string.d.ts.map +1 -1
  59. package/dist/esm/components/data-display/formatters/display-field-string.js +51 -12
  60. package/dist/esm/components/data-display/formatters/display-field-string.js.map +1 -1
  61. package/dist/esm/components/data-display/untrusted-html-content.d.ts +55 -0
  62. package/dist/esm/components/data-display/untrusted-html-content.d.ts.map +1 -0
  63. package/dist/esm/components/data-display/untrusted-html-content.js +67 -0
  64. package/dist/esm/components/data-display/untrusted-html-content.js.map +1 -0
  65. package/dist/esm/components/form/chromes/form-primitive.labels.d.ts.map +1 -1
  66. package/dist/esm/components/form/chromes/form-primitive.labels.js +9 -0
  67. package/dist/esm/components/form/chromes/form-primitive.labels.js.map +1 -1
  68. package/dist/esm/components/form/combobox.d.ts.map +1 -1
  69. package/dist/esm/components/form/combobox.js +10 -10
  70. package/dist/esm/components/form/combobox.js.map +1 -1
  71. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.adapter.d.ts +42 -0
  72. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.adapter.d.ts.map +1 -0
  73. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.adapter.js +120 -0
  74. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.adapter.js.map +1 -0
  75. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.d.ts +16 -0
  76. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.d.ts.map +1 -0
  77. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.js +8 -0
  78. package/dist/esm/components/form/controls/address/providers/google-places-address-lookup.js.map +1 -0
  79. package/dist/esm/components/navigation/app-level/activity-bar.d.ts.map +1 -1
  80. package/dist/esm/components/navigation/app-level/activity-bar.js +2 -2
  81. package/dist/esm/components/navigation/app-level/activity-bar.js.map +1 -1
  82. package/dist/esm/components/navigation/app-level/menu-user-org.d.ts.map +1 -1
  83. package/dist/esm/components/navigation/app-level/menu-user-org.js +10 -4
  84. package/dist/esm/components/navigation/app-level/menu-user-org.js.map +1 -1
  85. package/dist/esm/components/navigation/app-level/menu-user-org.labels.d.ts.map +1 -1
  86. package/dist/esm/components/navigation/app-level/menu-user-org.labels.js +7 -0
  87. package/dist/esm/components/navigation/app-level/menu-user-org.labels.js.map +1 -1
  88. package/dist/esm/components/navigation/app-level/menubar.d.ts.map +1 -1
  89. package/dist/esm/components/navigation/app-level/menubar.js +6 -6
  90. package/dist/esm/components/navigation/app-level/menubar.js.map +1 -1
  91. package/dist/esm/components/navigation/app-level/navigation-back.d.ts.map +1 -1
  92. package/dist/esm/components/navigation/app-level/navigation-back.js +27 -9
  93. package/dist/esm/components/navigation/app-level/navigation-back.js.map +1 -1
  94. package/dist/esm/components/navigation/app-level/sidebar.js +3 -3
  95. package/dist/esm/components/navigation/app-level/sidebar.js.map +1 -1
  96. package/dist/esm/components/navigation/app-level/status-bar.js +2 -2
  97. package/dist/esm/components/navigation/app-level/status-bar.js.map +1 -1
  98. package/dist/esm/components/navigation/app-level/toolbar.d.ts.map +1 -1
  99. package/dist/esm/components/navigation/app-level/toolbar.js +2 -2
  100. package/dist/esm/components/navigation/app-level/toolbar.js.map +1 -1
  101. package/dist/esm/components/navigation/app-level/utility-panel.js +2 -2
  102. package/dist/esm/components/navigation/app-level/utility-panel.js.map +1 -1
  103. package/dist/esm/components/navigation/breadcrumb.js +1 -1
  104. package/dist/esm/components/navigation/breadcrumb.js.map +1 -1
  105. package/dist/esm/components/sections/stepper.d.ts.map +1 -1
  106. package/dist/esm/components/sections/stepper.js +5 -3
  107. package/dist/esm/components/sections/stepper.js.map +1 -1
  108. package/dist/esm/config/preset-navigation.d.ts.map +1 -1
  109. package/dist/esm/config/preset-navigation.js +23 -2
  110. package/dist/esm/config/preset-navigation.js.map +1 -1
  111. package/dist/esm/config/resources/agent-work.resource-presets.d.ts +4 -0
  112. package/dist/esm/config/resources/agent-work.resource-presets.d.ts.map +1 -0
  113. package/dist/esm/config/resources/agent-work.resource-presets.js +115 -0
  114. package/dist/esm/config/resources/agent-work.resource-presets.js.map +1 -0
  115. package/dist/esm/config/resources/api-key-rotation-window.d.ts +22 -0
  116. package/dist/esm/config/resources/api-key-rotation-window.d.ts.map +1 -0
  117. package/dist/esm/config/resources/api-key-rotation-window.js +45 -0
  118. package/dist/esm/config/resources/api-key-rotation-window.js.map +1 -0
  119. package/dist/esm/config/resources/application.resource-presets.d.ts.map +1 -1
  120. package/dist/esm/config/resources/application.resource-presets.js +147 -61
  121. package/dist/esm/config/resources/application.resource-presets.js.map +1 -1
  122. package/dist/esm/config/resources/billing.resource-presets.d.ts +4 -3
  123. package/dist/esm/config/resources/billing.resource-presets.d.ts.map +1 -1
  124. package/dist/esm/config/resources/billing.resource-presets.js +240 -111
  125. package/dist/esm/config/resources/billing.resource-presets.js.map +1 -1
  126. package/dist/esm/config/resources/conversation-threads.resource-presets.d.ts +4 -0
  127. package/dist/esm/config/resources/conversation-threads.resource-presets.d.ts.map +1 -0
  128. package/dist/esm/config/resources/conversation-threads.resource-presets.js +105 -0
  129. package/dist/esm/config/resources/conversation-threads.resource-presets.js.map +1 -0
  130. package/dist/esm/config/resources/guidance.resource-presets.d.ts.map +1 -1
  131. package/dist/esm/config/resources/guidance.resource-presets.js +18 -11
  132. package/dist/esm/config/resources/guidance.resource-presets.js.map +1 -1
  133. package/dist/esm/config/resources/identity.resource-presets.d.ts +0 -13
  134. package/dist/esm/config/resources/identity.resource-presets.d.ts.map +1 -1
  135. package/dist/esm/config/resources/identity.resource-presets.js +48 -16
  136. package/dist/esm/config/resources/identity.resource-presets.js.map +1 -1
  137. package/dist/esm/config/resources/inbound-deliveries.resource-presets.d.ts +6 -0
  138. package/dist/esm/config/resources/inbound-deliveries.resource-presets.d.ts.map +1 -0
  139. package/dist/esm/config/resources/inbound-deliveries.resource-presets.js +112 -0
  140. package/dist/esm/config/resources/inbound-deliveries.resource-presets.js.map +1 -0
  141. package/dist/esm/config/resources/inbound-email.resource-presets.d.ts +4 -0
  142. package/dist/esm/config/resources/inbound-email.resource-presets.d.ts.map +1 -0
  143. package/dist/esm/config/resources/inbound-email.resource-presets.js +182 -0
  144. package/dist/esm/config/resources/inbound-email.resource-presets.js.map +1 -0
  145. package/dist/esm/config/resources/index.d.ts.map +1 -1
  146. package/dist/esm/config/resources/index.js +9 -2
  147. package/dist/esm/config/resources/index.js.map +1 -1
  148. package/dist/esm/config/resources/legal-holds.resource-presets.d.ts +4 -0
  149. package/dist/esm/config/resources/legal-holds.resource-presets.d.ts.map +1 -0
  150. package/dist/esm/config/resources/legal-holds.resource-presets.js +76 -0
  151. package/dist/esm/config/resources/legal-holds.resource-presets.js.map +1 -0
  152. package/dist/esm/config/resources/organizations.resource-presets.d.ts.map +1 -1
  153. package/dist/esm/config/resources/organizations.resource-presets.js +139 -36
  154. package/dist/esm/config/resources/organizations.resource-presets.js.map +1 -1
  155. package/dist/esm/config/resources/resource.config.utils.d.ts +20 -0
  156. package/dist/esm/config/resources/resource.config.utils.d.ts.map +1 -1
  157. package/dist/esm/config/resources/resource.config.utils.js +20 -0
  158. package/dist/esm/config/resources/resource.config.utils.js.map +1 -1
  159. package/dist/esm/config/resources/retention-enforcement.resource-presets.d.ts +5 -0
  160. package/dist/esm/config/resources/retention-enforcement.resource-presets.d.ts.map +1 -0
  161. package/dist/esm/config/resources/retention-enforcement.resource-presets.js +74 -0
  162. package/dist/esm/config/resources/retention-enforcement.resource-presets.js.map +1 -0
  163. package/dist/esm/config/resources/security.resource-presets.d.ts +3 -1
  164. package/dist/esm/config/resources/security.resource-presets.d.ts.map +1 -1
  165. package/dist/esm/config/resources/security.resource-presets.js +563 -133
  166. package/dist/esm/config/resources/security.resource-presets.js.map +1 -1
  167. package/dist/esm/config/resources/service-providers.resource-presets.d.ts +4 -0
  168. package/dist/esm/config/resources/service-providers.resource-presets.d.ts.map +1 -0
  169. package/dist/esm/config/resources/service-providers.resource-presets.js +321 -0
  170. package/dist/esm/config/resources/service-providers.resource-presets.js.map +1 -0
  171. package/dist/esm/config/resources/webhooks.resource-presets.d.ts +4 -3
  172. package/dist/esm/config/resources/webhooks.resource-presets.d.ts.map +1 -1
  173. package/dist/esm/config/resources/webhooks.resource-presets.js +76 -25
  174. package/dist/esm/config/resources/webhooks.resource-presets.js.map +1 -1
  175. package/dist/esm/index.d.ts +1 -0
  176. package/dist/esm/index.d.ts.map +1 -1
  177. package/dist/esm/index.js +1 -0
  178. package/dist/esm/index.js.map +1 -1
  179. package/dist/esm/registration/register-common-enums.d.ts.map +1 -1
  180. package/dist/esm/registration/register-common-enums.js +15 -0
  181. package/dist/esm/registration/register-common-enums.js.map +1 -1
  182. package/dist/esm/registration/register-provider-components.d.ts.map +1 -1
  183. package/dist/esm/registration/register-provider-components.js +6 -0
  184. package/dist/esm/registration/register-provider-components.js.map +1 -1
  185. package/dist/esm/themes/dense.theme.d.ts.map +1 -1
  186. package/dist/esm/themes/dense.theme.js +22 -2
  187. package/dist/esm/themes/dense.theme.js.map +1 -1
  188. package/dist/tsconfig.build.tsbuildinfo +1 -1
  189. package/package.json +8 -6
@@ -1,27 +1,65 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { resourceUIBehavior, resourceUIBehaviorFrom, polymorphicResourceUIBehavior, polymorphicResourceUIBehaviorFrom, StandardIconReference, ResourceOperationActionScope, ActionVisibility, ResourceLayoutInlineEditDefault, CollectionDisplayMode, } from '@wildo-ai/saas-frontend-lib';
3
- import { DisplayField, DisplayLayout, FormField, FormLayout, ObjectScope, Section, SummaryLayout, useFormTemplateContext, useDisplayTemplateContext, ResourceLayoutPreset_Create_SamlWizard, ResourceLayoutPreset_Read_SsoSaml, SummaryFlow, SummaryMeta, SummaryTitle, } from '@wildo-ai/saas-frontend-lib';
4
- import { BadgeSemanticVariant, BooleanDisplayMode, BooleanEditMode, BooleanLabellingValue, SectionMode, DisplayChromeMode, EditChromeMode, EnumDisplayMode, ShowWhenOperator, StringDisplayMode, } from '@wildo-ai/zod-decorators';
5
- import { Key, ShieldCheck, Globe, Unlink, Fingerprint, UserCog, KeyRound, Radio, AlertTriangle, RotateCw, RefreshCw, PowerOff, Power, CalendarPlus, ChartColumn, Download, Trash2, PlugZap, Search, ShieldX, } from 'lucide-react';
6
- import { CoreResourceType as R, CoreResourceOperation as Op, ApiKeyStatus, OrgApiKeys_Operations, AuditLogs_Operations, SiemDeliveryDlq_Operations, SsoConnection_Operations, ScimTokens_Operations, orgApiKeysResourceConfiguration_InitializationFactory as orgApiKeysFactory, orgProviderCredentialsResourceConfiguration_InitializationFactory as orgProviderCredentialsFactory, OrganizationProviderCredentialSchema, userProviderCredentialsResourceConfiguration_InitializationFactory as userProviderCredentialsFactory, UserProviderCredentialSchema, ProviderCredentialValidationState, auditLogsResourceConfiguration_InitializationFactory as auditLogsFactory, applicationAuditLogsResourceConfiguration_InitializationFactory as applicationAuditLogsFactory, siemExportConfigPolymorphicFactories, siemDeliveryDlqPolymorphicFactories, ssoConfigPolymorphicFactories, SsoConfig_Operations, ssoConnectionPolymorphicFactories, scimProvisioningConfigPolymorphicFactories, scimTokensPolymorphicFactories, ApiKeyOrganizationSchema, AuditableEvent_BaseSchema, SiemExportConfigBaseSchema, SiemDeliveryDlqBaseSchema, SiemDeliveryDlqStatus, SsoConfigBaseSchema, SsoConnectionBaseSchema, ScimProvisioningConfigBaseSchema, ScimTokenBaseSchema, SsoProtocol, SIEMAuthMethod, } from '@wildo-ai/saas-models';
7
- import { addressable, notAddressable } from './resource.config.utils.js';
2
+ /**
3
+ * Security & Compliance — Resource frontend presets
4
+ *
5
+ * Domain: ORG_API_KEYS, OAUTH_CLIENTS_ORGANIZATION, AUDIT_LOGS, SIEM (polymorphic),
6
+ * SSO_CONFIG (poly), SSO_CONNECTIONS (poly), SCIM_PROVISIONING (poly), SCIM_TOKENS (poly)
7
+ *
8
+ * SIEM layout JSX lives in this file next to `layout.refs` + `fields` so section chrome
9
+ * and primitive UI params stay co-located with the preset. Edit/display templates are
10
+ * inlined as arrow functions so `useFormTemplateContext` / `useDisplayTemplateContext`
11
+ * are immediately available for future conditional rendering. The scope-level CONFIGURATION records
12
+ * here — SIEM export, SSO config, SCIM provisioning — declare the full settings chrome
13
+ * (`display` + `summary` + `edit`), because each is reached as a READ inside the Settings Hub and is
14
+ * opened to confirm a value far more often than to change one; only sections that differ (e.g. the
15
+ * status toggle) set `chromeMode` explicitly. The COLLECTIONS beside them (SSO connections, the SIEM
16
+ * dead-letter queue, SCIM tokens) declare none on purpose: a list has no label/value rows for the
17
+ * treatment to apply to.
18
+ * Other resources here use field-level UI only until they need custom sections; enum badges use
19
+ * `showDescription: true` like billing.
20
+ */
21
+ import React from 'react';
22
+ import { resolveVocabularyWordKey } from '@wildo-ai/presets-components-models';
23
+ import { resourceUIBehavior, resourceUIBehaviorFrom, polymorphicResourceUIBehavior, polymorphicResourceUIBehaviorFrom, StandardIconReference, ResourceOperationActionScope, ActionVisibility, ResourceLayoutInlineEditDefault, CollectionDisplayMode, useFlowsActorsSafe, unresolvedLabel, } from '@wildo-ai/saas-frontend-lib';
24
+ import { DisplayField, DisplayLayout, FormField, FormLayout, ObjectScope, Section, SummaryLayout, useFormTemplateContext, useDisplayTemplateContext, ResourceLayoutPreset_Create_SamlWizard, ResourceLayoutPreset_Read_SsoSaml, SummaryFlow, SummaryMeta, SummaryTitle, ResourceHeaderTitle, } from '@wildo-ai/saas-frontend-lib';
25
+ import { BadgeSemanticVariant, BooleanDisplayMode, BooleanEditMode, BooleanLabellingValue, SectionMode, DisplayChromeMode, EditChromeMode, EnumDisplayMode, ShowWhenOperator, StringDisplayMode, StringEditMode, } from '@wildo-ai/zod-decorators';
26
+ import { Key, ShieldCheck, Globe, Unlink, Fingerprint, UserCog, KeyRound, Radio, AlertTriangle, RotateCw, RefreshCw, PowerOff, Power, CalendarPlus, ChartColumn, Download, Trash2, PlugZap, Search, ShieldX, Bot, } from 'lucide-react';
27
+ import { CoreResourceType as R, CoreResourceOperation as Op, ApiKeyStatus, OrgApiKeys_Operations, AuditLogs_Operations, SiemDeliveryDlq_Operations, SsoConnection_Operations, ScimTokens_Operations, orgApiKeysResourceConfiguration_InitializationFactory as orgApiKeysFactory, orgOAuthClientsResourceConfiguration_InitializationFactory as orgOAuthClientsFactory, OAuthClientOrganizationSchema, OAuthClientStatus, OrgOAuthClients_Operations, orgProviderCredentialsResourceConfiguration_InitializationFactory as orgProviderCredentialsFactory, OrganizationProviderCredentialSchema, userProviderCredentialsResourceConfiguration_InitializationFactory as userProviderCredentialsFactory, UserProviderCredentialSchema, ProviderCredentialValidationState, auditLogsResourceConfiguration_InitializationFactory as auditLogsFactory, applicationAuditLogsResourceConfiguration_InitializationFactory as applicationAuditLogsFactory, siemExportConfigPolymorphicFactories, siemDeliveryDlqPolymorphicFactories, ssoConfigPolymorphicFactories, SsoConfig_Operations, ssoConnectionPolymorphicFactories, scimProvisioningConfigPolymorphicFactories, scimTokensPolymorphicFactories, ApiKeyOrganizationSchema, AuditableEvent_BaseSchema, AuditEventSeverity, SiemExportConfigBaseSchema, SiemDeliveryDlqBaseSchema, SiemDeliveryDlqStatus, SsoConfigBaseSchema, SsoConnectionBaseSchema, ScimProvisioningConfigBaseSchema, ScimTokenBaseSchema, SsoProtocol, SIEMAuthMethod, EngineOpenVocabulary, } from '@wildo-ai/saas-models';
28
+ import { ApiKeyRotationWindowRows } from './api-key-rotation-window.js';
29
+ import { addressable, notAddressable, ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE } from './resource.config.utils.js';
30
+ /*
31
+ * A registry — keys, connections, tokens — is comparable rows, so it renders as a TABLE with the headers written
32
+ * once, and cards stay one click away for a narrow screen. Named once because the admin lane restates each
33
+ * registry's LIST view and must not lose it.
34
+ */
35
+ const REGISTRY_TABLE = {
36
+ displayMode: CollectionDisplayMode.TABLE,
37
+ userSwitchableDisplayMode: true,
38
+ };
8
39
  // ── SIEM export config — field overrides (shared across polymorphic variants) ─
9
40
  const siemExportFieldOverrides = {
41
+ // Field descriptions are an EDIT aid — they say what to type. On the read pane, which an operator opens to
42
+ // confirm a value, they were a sentence under every row ("Controls whether event deliveries are sent." under
43
+ // "Enabled"), so `display` no longer asks for them; the form keeps them.
44
+ //
45
+ // On the card the value reads bare, and a bare checkmark says nothing about WHAT is true — so the summary
46
+ // renders the word ("Enabled" / "Disabled") instead of a label and a tick.
10
47
  enabled: (schema) => schema.shape.enabled.booleanUI({
11
48
  labellingValue: BooleanLabellingValue.ENABLED_DISABLED,
12
- display: { displayMode: BooleanDisplayMode.CHECKMARK, showDescription: true },
49
+ display: { displayMode: BooleanDisplayMode.CHECKMARK },
50
+ summaryOverride: { displayMode: BooleanDisplayMode.RAW },
13
51
  edit: { editMode: BooleanEditMode.SWITCH, showDescription: true },
14
52
  }),
15
53
  webhookUrl: (schema) => schema.shape.webhookUrl.stringUI({
16
- display: { displayMode: StringDisplayMode.COPY, showDescription: true },
54
+ display: { displayMode: StringDisplayMode.COPY },
17
55
  edit: { showDescription: true },
18
56
  }),
19
57
  authMethod: (schema) => schema.shape.authMethod.enumUI({
20
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
58
+ display: { displayMode: EnumDisplayMode.BADGE },
21
59
  edit: { showDescription: true, tooltip: true },
22
60
  }),
23
61
  eventFormat: (schema) => schema.shape.eventFormat.enumUI({
24
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
62
+ display: { displayMode: EnumDisplayMode.BADGE },
25
63
  edit: { showDescription: true },
26
64
  }),
27
65
  authCredential: (schema) => schema.shape.authCredential.showWhen({
@@ -31,26 +69,137 @@ const siemExportFieldOverrides = {
31
69
  conditions: { field: 'authMethod', value: SIEMAuthMethod.API_KEY_HEADER },
32
70
  }),
33
71
  };
72
+ /*
73
+ * An API key is read to decide whether to rotate, extend or retire it. The header is its NAME and its STATUS
74
+ * badge, so neither is a row below it. `scope` is not rendered anywhere: the organization schema fixes it to one
75
+ * literal, so on this screen it can only ever say "organization" — it rendered as an empty "Scope: —" on every
76
+ * card. The first card has no heading (it would restate the header) and no read section carries a description.
77
+ *
78
+ * `access` is gone as a section: it held the roles and the description, and a heading over the one row that
79
+ * matters ("Access" above "Roles") was a heading per field. Both now sit in the first card, beside the prefix.
80
+ */
81
+ function OrgApiKeyHeader() {
82
+ return (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-field", children: [_jsx(ResourceHeaderTitle, {}), _jsx(DisplayField, { name: "status", hideLabel: true, hideDescription: true, className: "w-auto" })] }));
83
+ }
34
84
  function OrgApiKeyDisplayTemplate() {
35
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "name" }), _jsx(DisplayField, { name: "scope" }), _jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "keyPrefix" })] }), _jsxs(Section, { name: "access", children: [_jsx(DisplayField, { name: "roles" }), _jsx(DisplayField, { name: "description" })] }), _jsxs(Section, { name: "lifecycle", children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "createdAt" })] })] }));
85
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "keyPrefix" }), _jsx(DisplayField, { name: "roles" }), _jsx(DisplayField, { name: "description" })] }), _jsxs(Section, { name: "lifecycle", hideDescription: true, children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "createdAt" }), _jsx(ApiKeyRotationWindowRows, {})] })] }));
36
86
  }
87
+ /*
88
+ * The CREATE form (this resource has no UPDATE: a key changes through its own verbs — rotate, deactivate,
89
+ * extend). One column, no headings: five fields do not need grouping, and a heading over two of them named
90
+ * the page. The name first and focused; the description is prose, so a textarea; status last, since a new key
91
+ * is active unless somebody deliberately says otherwise.
92
+ */
37
93
  function OrgApiKeyEditTemplate() {
38
- return (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(FormField, { name: "name" }), _jsx(FormField, { name: "status" }), _jsx(FormField, { name: "expiresAt" })] }), _jsxs(Section, { name: "access", children: [_jsx(FormField, { name: "roles" }), _jsx(FormField, { name: "description" })] })] }));
94
+ return (_jsxs(FormLayout, { children: [_jsx(FormField, { name: "name", autoFocus: true }), _jsx(FormField, { name: "description" }), _jsx(FormField, { name: "roles" }), _jsx(FormField, { name: "expiresAt" }), _jsx(FormField, { name: "status" })] }));
95
+ }
96
+ /*
97
+ * The autonomous agents a client may run as are the ones the APPLICATION declares (#1611), and the backend refuses
98
+ * any other ref — so the binding field offers exactly those, read from `/flows-actors/frontend-info`, instead of a
99
+ * free-text list an administrator fills from memory (#1812). Until the declaration arrives, or where the flows-actors
100
+ * plane is absent, the choice is empty rather than free text: there is nothing it would be honest to accept.
101
+ *
102
+ * Values are the refs themselves. They are identifiers the application names, not a closed engine vocabulary with
103
+ * words in the label tree, so a ref is what an administrator sees — and what every audit row records.
104
+ */
105
+ function DeclaredAutonomousAgentsFormField() {
106
+ const flowsActors = useFlowsActorsSafe();
107
+ // Asked for explicitly: the provider bootstraps the declaration only when the application configures a chat view,
108
+ // and an application with none (the scaffold, Wonder CRM) would otherwise offer an empty choice (#1812 review).
109
+ const requestAutonomousAgents = flowsActors?.requestAutonomousAgents;
110
+ React.useEffect(() => {
111
+ requestAutonomousAgents?.();
112
+ }, [requestAutonomousAgents]);
113
+ const declaredAutonomousAgentRefs = (flowsActors?.autonomousAgents ?? []).map((agent) => agent.ref);
114
+ return _jsx(FormField, { name: "autonomousAgentRefs", allowedValues: declaredAutonomousAgentRefs });
115
+ }
116
+ /*
117
+ * ONE flat form for every operation of an organization OAuth client, like an API key's. A field the operation does
118
+ * not accept hides itself, so CREATE shows the registration fields and `setAutonomousAgents` shows only the agent
119
+ * list (`autonomousAgentRefs` is excluded from CREATE: a client is registered first, then bound). Sections were
120
+ * rejected here: a section renders its heading whether or not anything under it survives, so the binding form would
121
+ * have carried "Access" and "Redirects" headings over nothing.
122
+ */
123
+ function OrgOAuthClientEditTemplate() {
124
+ return (_jsxs(FormLayout, { children: [_jsx(FormField, { name: "name", autoFocus: true }), _jsx(FormField, { name: "description" }), _jsx(FormField, { name: "expiresAt" }), _jsx(FormField, { name: "allowedGrants" }), _jsx(FormField, { name: "allowedScopes" }), _jsx(FormField, { name: "roles" }), _jsx(FormField, { name: "tokenEndpointAuthMethod" }), _jsx(FormField, { name: "requiresConsent" }), _jsx(FormField, { name: "redirectUris" }), _jsx(FormField, { name: "postLogoutRedirectUris" }), _jsx(DeclaredAutonomousAgentsFormField, {})] }));
39
125
  }
126
+ /*
127
+ * A card: the name, then the status as a badge and the key's visible prefix. The prefix keeps its label — a
128
+ * bare "wkprobeB" beside a badge reads as a second name, not as the first characters of the secret.
129
+ */
40
130
  function OrgApiKeySummaryTemplate() {
41
- return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "scope" }), _jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "keyPrefix" })] })] }));
131
+ return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "keyPrefix" })] })] }));
42
132
  }
133
+ /*
134
+ * An audit event, read once for what happened and once for who did it. The header is its NAME — the event type
135
+ * and the instant, in the viewer's date-time format — so neither is a row below it, and the overview card has no
136
+ * heading of its own (it would restate the header). No read section carries a description: "Review all
137
+ * application activity" and "See who performed each action" were prompts, on a page that already shows it.
138
+ * Every actor row hides when empty — most events have one actor, and a column of dashes said nothing.
139
+ */
43
140
  function AuditLogDisplayTemplate() {
44
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "eventType" }), _jsx(DisplayField, { name: "eventCategory" }), _jsx(DisplayField, { name: "severity" }), _jsx(DisplayField, { name: "riskLevel" })] }), _jsxs(Section, { name: "actors", children: [_jsx(DisplayField, { name: "userId" }), _jsx(DisplayField, { name: "organizationId" })] }), _jsxs(Section, { name: "payload", children: [_jsx(DisplayField, { name: "eventData" }), _jsx(DisplayField, { name: "createdAt" })] })] }));
141
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "eventCategory" }), _jsx(DisplayField, { name: "severity" }), _jsx(DisplayField, { name: "riskLevel" })] }), _jsxs(Section, { name: "actors", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "actorKind" }), _jsx(DisplayField, { name: "userId" }), _jsx(DisplayField, { name: "agentActor" }), _jsx(DisplayField, { name: "delegatingClientId" }), _jsx(DisplayField, { name: "principalCredentialId" }), _jsx(DisplayField, { name: "actingViaServiceProviderRelationshipId" }), _jsx(DisplayField, { name: "providerOrganizationId" }), _jsx(DisplayField, { name: "organizationId" })] }), _jsx(Section, { name: "payload", appearance: SectionMode.CARD, chromeMode: DisplayChromeMode.HIDDEN, hideDescription: true, children: _jsx(DisplayField, { name: "eventData" }) })] }));
45
142
  }
143
+ /*
144
+ * A card in a list grouped by DAY: the event type, then its severity as a badge and who acted, bare. The date is
145
+ * the group heading above it, so the card does not repeat it; the exact instant is the read page's name.
146
+ */
46
147
  function AuditLogSummaryTemplate() {
47
- return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "eventType" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "eventCategory" }), _jsx(DisplayField, { name: "severity" }), _jsx(DisplayField, { name: "createdAt" })] })] }));
148
+ return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "eventType" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "severity" }), _jsx(DisplayField, { name: "eventCategory" }), _jsx(DisplayField, { name: "actorKind" })] })] }));
149
+ }
150
+ /**
151
+ * An audit event's name, shared by both audit doors: the WORD for its type (`system_enums.CoreAuditableEventType.<type>`,
152
+ * #1791) and the instant it happened, in the viewer's date-time format — many events share a day, so the time is part
153
+ * of the name. `eventType` used to be printed raw (`user_login`); an event type with no word still shows its value,
154
+ * which is a missing label looking missing. `eventType` is required on every event.
155
+ */
156
+ function auditEventName({ currentObject, t, formatDateTime }) {
157
+ const wordKey = resolveVocabularyWordKey(EngineOpenVocabulary.CORE_AUDITABLE_EVENT_TYPE, currentObject.eventType);
158
+ const word = t(wordKey);
159
+ const type = word === wordKey ? unresolvedLabel(currentObject.eventType) : word;
160
+ return currentObject.createdAt ? `${type} — ${formatDateTime(currentObject.createdAt)}` : type;
161
+ }
162
+ /**
163
+ * Chrome and field decorators shared by both audit doors (tenant and application-wide): one object, one reading.
164
+ * Settings rows on the read page, bare values on a card, empty actor rows hidden, severity as a coloured badge.
165
+ */
166
+ function auditLogPresentation(schemaShape) {
167
+ const hideWhenEmpty = { display: { hideWhenEmpty: true } };
168
+ return {
169
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
170
+ fields: {
171
+ severity: schemaShape.severity.enumUI({
172
+ display: { displayMode: EnumDisplayMode.BADGE },
173
+ values: {
174
+ [AuditEventSeverity.INFO]: { color: BadgeSemanticVariant.SECONDARY },
175
+ [AuditEventSeverity.WARNING]: { color: BadgeSemanticVariant.WARNING },
176
+ [AuditEventSeverity.CRITICAL]: { color: BadgeSemanticVariant.DESTRUCTIVE },
177
+ },
178
+ }),
179
+ userId: schemaShape.userId.stringUI(hideWhenEmpty),
180
+ agentActor: schemaShape.agentActor.objectUI(hideWhenEmpty),
181
+ delegatingClientId: schemaShape.delegatingClientId.stringUI(hideWhenEmpty),
182
+ principalCredentialId: schemaShape.principalCredentialId.stringUI(hideWhenEmpty),
183
+ actingViaServiceProviderRelationshipId: schemaShape.actingViaServiceProviderRelationshipId.stringUI(hideWhenEmpty),
184
+ providerOrganizationId: schemaShape.providerOrganizationId.stringUI(hideWhenEmpty),
185
+ organizationId: schemaShape.organizationId.stringUI(hideWhenEmpty),
186
+ },
187
+ };
48
188
  }
49
189
  function SiemExportSummaryTemplate() {
50
190
  return (_jsx(SummaryLayout, { flow: SummaryFlow.ROW, children: _jsx(DisplayField, { name: "enabled" }) }));
51
191
  }
192
+ /*
193
+ * A failed delivery is named by its failure reason and carries its status badge beside it, so the old
194
+ * `overview` card — which held exactly those two — is gone rather than repeating the header. What is left is
195
+ * the attempt record and the payload. The payload card's heading names the data, so the data itself carries no
196
+ * label (the audit trail's `payload` card, for the same reason).
197
+ */
198
+ function SiemDlqHeader() {
199
+ return (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-field", children: [_jsx(ResourceHeaderTitle, {}), _jsx(DisplayField, { name: "status", hideLabel: true, hideDescription: true, className: "w-auto" })] }));
200
+ }
52
201
  function SiemDlqDisplayTemplate() {
53
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "failureReason" })] }), _jsxs(Section, { name: "delivery", children: [_jsx(DisplayField, { name: "eventId" }), _jsx(DisplayField, { name: "failureCount" }), _jsx(DisplayField, { name: "lastAttemptAt" }), _jsx(DisplayField, { name: "nextRetryAt" })] }), _jsx(Section, { name: "payload", children: _jsx(DisplayField, { name: "eventPayload" }) })] }));
202
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "delivery", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "eventId" }), _jsx(DisplayField, { name: "failureCount" }), _jsx(DisplayField, { name: "lastAttemptAt" }), _jsx(DisplayField, { name: "nextRetryAt" })] }), _jsx(Section, { name: "payload", appearance: SectionMode.CARD, chromeMode: DisplayChromeMode.HIDDEN, hideDescription: true, children: _jsx(DisplayField, { name: "eventPayload" }) })] }));
54
203
  }
55
204
  function SiemDlqSummaryTemplate() {
56
205
  return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "failureReason" }), _jsx(SummaryMeta, { children: _jsx(DisplayField, { name: "status" }) })] }));
@@ -61,41 +210,200 @@ function SsoConfigDisplayTemplate() {
61
210
  function SsoConfigSummaryTemplate() {
62
211
  return (_jsx(SummaryLayout, { flow: SummaryFlow.ROW, children: _jsx(ObjectScope, { name: "enforcement", children: _jsx(DisplayField, { name: "requireSSO" }) }) }));
63
212
  }
213
+ /*
214
+ * A connection is named by its display name and carries its protocol badge beside it, so neither is a row; the
215
+ * first card, which held both, keeps only what the header does not say and loses its heading. Only the section
216
+ * of the connection's OWN protocol renders: an OIDC connection showed a whole SAML section of empty rows (and
217
+ * the reverse), which read as unfinished configuration rather than as not applicable. No read section carries a
218
+ * description.
219
+ */
220
+ function SsoConnectionHeader() {
221
+ return (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-field", children: [_jsx(ResourceHeaderTitle, {}), _jsx(DisplayField, { name: "protocol", hideLabel: true, hideDescription: true, className: "w-auto" })] }));
222
+ }
64
223
  function SsoConnectionDisplayTemplate() {
65
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "displayName" }), _jsx(DisplayField, { name: "protocol" }), _jsx(DisplayField, { name: "enabled" }), _jsx(DisplayField, { name: "isDefault" }), _jsx(DisplayField, { name: "externalProviderId" })] }), _jsx(Section, { name: "oidc", children: _jsxs(ObjectScope, { name: "oidc", children: [_jsx(DisplayField, { name: "templateMode" }), _jsx(DisplayField, { name: "discoveryUrl" }), _jsx(DisplayField, { name: "issuer" }), _jsx(DisplayField, { name: "authorizationEndpoint" }), _jsx(DisplayField, { name: "tokenEndpoint" }), _jsx(DisplayField, { name: "userInfoEndpoint" }), _jsx(DisplayField, { name: "jwksUri" }), _jsx(DisplayField, { name: "endSessionEndpoint" }), _jsx(DisplayField, { name: "clientId" }), _jsx(DisplayField, { name: "scopes" }), _jsx(DisplayField, { name: "responseType" }), _jsx(DisplayField, { name: "responseMode" }), _jsx(DisplayField, { name: "pkceEnabled" }), _jsx(DisplayField, { name: "pkceMethod" }), _jsx(DisplayField, { name: "prompt" }), _jsx(DisplayField, { name: "acrValues" }), _jsx(DisplayField, { name: "maxAge" }), _jsx(DisplayField, { name: "claimsMapping" }), _jsx(DisplayField, { name: "auditSettings" })] }) }), _jsx(Section, { name: "saml", children: _jsxs(ObjectScope, { name: "saml", children: [_jsx(DisplayField, { name: "entityId" }), _jsx(DisplayField, { name: "ssoUrl" }), _jsx(DisplayField, { name: "sloUrl" }), _jsx(DisplayField, { name: "x509CertificatePem" }), _jsx(DisplayField, { name: "attributeMapping" }), _jsx(DisplayField, { name: "signRequests" }), _jsx(DisplayField, { name: "wantAssertionsSigned" }), _jsx(DisplayField, { name: "allowedClockDrift" }), _jsx(DisplayField, { name: "forceAuthn" }), _jsx(DisplayField, { name: "maxAuthAge" }), _jsx(DisplayField, { name: "nameIdFormat" }), _jsx(DisplayField, { name: "allowIdpInitiated" }), _jsx(DisplayField, { name: "auditSettings" })] }) }), _jsxs(Section, { name: "provisioning", children: [_jsx(DisplayField, { name: "autoCreateUsers" }), _jsx(DisplayField, { name: "autoVerifyEmail" }), _jsx(DisplayField, { name: "defaultRole" }), _jsx(DisplayField, { name: "allowedDomains" }), _jsx(DisplayField, { name: "roleMapping" }), _jsx(DisplayField, { name: "groupMapping" })] })] }));
224
+ const { data } = useDisplayTemplateContext();
225
+ const protocol = data?.protocol;
226
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "enabled" }), _jsx(DisplayField, { name: "isDefault" }), _jsx(DisplayField, { name: "externalProviderId" })] }), protocol !== SsoProtocol.SAML && (_jsx(Section, { name: "oidc", hideDescription: true, children: _jsxs(ObjectScope, { name: "oidc", children: [_jsx(DisplayField, { name: "templateMode" }), _jsx(DisplayField, { name: "discoveryUrl" }), _jsx(DisplayField, { name: "issuer" }), _jsx(DisplayField, { name: "authorizationEndpoint" }), _jsx(DisplayField, { name: "tokenEndpoint" }), _jsx(DisplayField, { name: "userInfoEndpoint" }), _jsx(DisplayField, { name: "jwksUri" }), _jsx(DisplayField, { name: "endSessionEndpoint" }), _jsx(DisplayField, { name: "clientId" }), _jsx(DisplayField, { name: "scopes" }), _jsx(DisplayField, { name: "responseType" }), _jsx(DisplayField, { name: "responseMode" }), _jsx(DisplayField, { name: "pkceEnabled" }), _jsx(DisplayField, { name: "pkceMethod" }), _jsx(DisplayField, { name: "prompt" }), _jsx(DisplayField, { name: "acrValues" }), _jsx(DisplayField, { name: "maxAge" }), _jsx(DisplayField, { name: "claimsMapping" })] }) })), protocol !== SsoProtocol.OIDC && (_jsx(Section, { name: "saml", hideDescription: true, children: _jsxs(ObjectScope, { name: "saml", children: [_jsx(DisplayField, { name: "entityId" }), _jsx(DisplayField, { name: "ssoUrl" }), _jsx(DisplayField, { name: "sloUrl" }), _jsx(DisplayField, { name: "x509CertificatePem" }), _jsx(DisplayField, { name: "attributeMapping" }), _jsx(DisplayField, { name: "signRequests" }), _jsx(DisplayField, { name: "wantAssertionsSigned" }), _jsx(DisplayField, { name: "allowedClockDrift" }), _jsx(DisplayField, { name: "forceAuthn" }), _jsx(DisplayField, { name: "maxAuthAge" }), _jsx(DisplayField, { name: "nameIdFormat" }), _jsx(DisplayField, { name: "allowIdpInitiated" })] }) })), _jsxs(Section, { name: "provisioning", hideDescription: true, children: [_jsx(DisplayField, { name: "autoCreateUsers" }), _jsx(DisplayField, { name: "defaultRole" }), _jsx(DisplayField, { name: "allowedDomains" }), _jsx(DisplayField, { name: "roleMapping" }), _jsx(DisplayField, { name: "groupMapping" })] })] }));
66
227
  }
228
+ /* A card: the name, then the protocol badge and whether the connection is on — the latter as the word, see `enabled`. */
67
229
  function SsoConnectionSummaryTemplate() {
68
230
  return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "displayName" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "protocol" }), _jsx(DisplayField, { name: "enabled" })] })] }));
69
231
  }
232
+ /*
233
+ * The pane's header already names it ("SCIM provisioning"), so its groups carry no headings: the first card's
234
+ * was "Overview" — a name for the page, not for a part of it — the second's put "Attribute mapping" over a single
235
+ * row labelled "Attribute mapping", and the third's called two record timestamps "User lifecycle", which is what
236
+ * the FIRST card governs. No section carries a description on read.
237
+ */
70
238
  function ScimProvisioningDisplayTemplate() {
71
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "autoCreateUsers" }), _jsx(DisplayField, { name: "autoVerifyEmail" }), _jsx(DisplayField, { name: "autoDeactivateUsers" }), _jsx(DisplayField, { name: "defaultRole" })] }), _jsx(Section, { name: "mapping", children: _jsx(DisplayField, { name: "attributeMapping" }) }), _jsxs(Section, { name: "lifecycle", children: [_jsx(DisplayField, { name: "createdAt" }), _jsx(DisplayField, { name: "updatedAt" })] })] }));
239
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "autoCreateUsers" }), _jsx(DisplayField, { name: "autoVerifyEmail" }), _jsx(DisplayField, { name: "autoDeactivateUsers" }), _jsx(DisplayField, { name: "defaultRole" })] }), _jsx(Section, { name: "mapping", hideTitle: true, hideDescription: true, children: _jsx(DisplayField, { name: "attributeMapping" }) }), _jsxs(Section, { name: "lifecycle", hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "createdAt" }), _jsx(DisplayField, { name: "updatedAt" })] })] }));
72
240
  }
73
241
  function ScimProvisioningSummaryTemplate() {
74
242
  return (_jsx(SummaryLayout, { flow: SummaryFlow.ROW, children: _jsx(DisplayField, { name: "autoCreateUsers" }) }));
75
243
  }
244
+ /*
245
+ * A token is named by its display name (the header), so the first card keeps whether it is active and who
246
+ * created it, without a heading of its own. No section carries a description on read.
247
+ */
76
248
  function ScimTokenDisplayTemplate() {
77
- return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "displayName" }), _jsx(DisplayField, { name: "isActive" }), _jsx(DisplayField, { name: "createdByUserId" })] }), _jsxs(Section, { name: "lifecycle", children: [_jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "createdAt" })] })] }));
249
+ return (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "isActive" }), _jsx(DisplayField, { name: "createdByUserId" })] }), _jsxs(Section, { name: "lifecycle", hideDescription: true, children: [_jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "createdAt" })] })] }));
78
250
  }
79
251
  function ScimTokenSummaryTemplate() {
80
252
  return (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "displayName" }), _jsx(SummaryMeta, { children: _jsx(DisplayField, { name: "isActive" }) })] }));
81
253
  }
254
+ /*
255
+ * A stored third-party credential, at either owner scope (the tenant's, or a person's own). Its NAME is
256
+ * `provider · slot` and its validation state is the badge beside it, so neither the provider, the slot nor the
257
+ * state is a row: the card showed "asana · ASANA_API_KEY", then "Provider: asana", then "Secret key:
258
+ * ASANA_API_KEY" — the title, twice more.
259
+ *
260
+ * No `<Section>` anywhere, so no `layout.refs`: a section is a contract the specification has to document slot
261
+ * for slot, and a credential is a handful of facts that no heading would group better than the rows do. Every
262
+ * optional fact hides when empty — a credential nobody has validated or used yet is the ordinary case, and a
263
+ * column of dashes under it said nothing.
264
+ */
265
+ function ProviderCredentialHeader() {
266
+ return (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-field", children: [_jsx(ResourceHeaderTitle, {}), _jsx(DisplayField, { name: "validationState", hideLabel: true, hideDescription: true, className: "w-auto" })] }));
267
+ }
268
+ /** `setByUserId` exists on the tenant's credential only: a person's own is, by construction, set by them. */
269
+ function providerCredentialDisplayTemplate(includeSetBy) {
270
+ return function ProviderCredentialDisplayTemplate() {
271
+ return (_jsxs(DisplayLayout, { children: [_jsx(DisplayField, { name: "enabled" }), _jsx(DisplayField, { name: "description" }), _jsx(DisplayField, { name: "validationError" }), _jsx(DisplayField, { name: "lastValidatedAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "secretSetAt" }), includeSetBy && _jsx(DisplayField, { name: "setByUserId" }), _jsx(DisplayField, { name: "connectionFacts" }), _jsx(DisplayField, { name: "createdAt" })] }));
272
+ };
273
+ }
274
+ /*
275
+ * The form a person reaches from a credential's Edit action: replace the secret, switch it off, or note what
276
+ * it is for. The secret first and focused, since replacing it is why the form is usually opened. The provider
277
+ * and slot are not offered — they are the credential's identity and cannot change — except on the API's CREATE,
278
+ * which these screens hide (#1040) and which would otherwise have no way to name them.
279
+ */
280
+ function ProviderCredentialEditTemplate() {
281
+ const { isExisting } = useFormTemplateContext();
282
+ return (_jsxs(FormLayout, { children: [!isExisting && _jsx(FormField, { name: "providerRef" }), !isExisting && _jsx(FormField, { name: "secretSlug" }), _jsx(FormField, { name: "secretValue", autoFocus: true }), _jsx(FormField, { name: "enabled" }), _jsx(FormField, { name: "description" })] }));
283
+ }
284
+ /* A card: the name (`provider · slot`), then the validation badge and whether the credential is on, as the word. */
285
+ function ProviderCredentialSummaryTemplate() {
286
+ return (_jsx(SummaryLayout, { children: _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "validationState" }), _jsx(DisplayField, { name: "enabled" })] }) }));
287
+ }
288
+ /**
289
+ * Chrome and field decorators shared by both provider-credential scopes. Settings rows on read (a credential is
290
+ * inspected one fact at a time), bare values on a card, a stacked form. `enabled` reads as the word on a card —
291
+ * a bare checkmark there says nothing about WHAT is true.
292
+ */
293
+ function providerCredentialPresentation(schemaShape) {
294
+ const hideWhenEmpty = { display: { hideWhenEmpty: true } };
295
+ return {
296
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
297
+ fields: {
298
+ enabled: schemaShape.enabled.booleanUI({
299
+ labellingValue: BooleanLabellingValue.ENABLED_DISABLED,
300
+ summaryOverride: { displayMode: BooleanDisplayMode.RAW },
301
+ }),
302
+ description: schemaShape.description.stringUI({
303
+ ...hideWhenEmpty,
304
+ edit: { editMode: StringEditMode.TEXTAREA },
305
+ }),
306
+ validationError: schemaShape.validationError.stringUI(hideWhenEmpty),
307
+ lastValidatedAt: schemaShape.lastValidatedAt.dateUI(hideWhenEmpty),
308
+ lastUsedAt: schemaShape.lastUsedAt.dateUI(hideWhenEmpty),
309
+ secretSetAt: schemaShape.secretSetAt.dateUI(hideWhenEmpty),
310
+ connectionFacts: schemaShape.connectionFacts.objectUI(hideWhenEmpty),
311
+ },
312
+ };
313
+ }
82
314
  export function buildSecurityMainPresets() {
83
315
  return {
316
+ /**
317
+ * An organization's OAuth clients — the machine credentials it registers for itself, and the ones an autonomous
318
+ * agent runs as (#1611). Until #1812 they had no screen, so creating, rotating or ending a client — and binding an
319
+ * agent to one — was REST only.
320
+ *
321
+ * The application-scope preset (`application.resource-presets.tsx`) is the model, section for section, because the
322
+ * two are one credential type at two scopes. Two differences, each for the resource's own reason:
323
+ * - an `autonomousAgents` section: which agents this client runs is a grant of standing authority to act
324
+ * unattended, so it is on the page rather than behind the action that changes it;
325
+ * - the Settings Hub chrome of an API key (stacked form, table registry), because this is where it sits — beside
326
+ * API keys, in the organization's settings — rather than on the application's administration page.
327
+ *
328
+ * `EXPIRE` is the expiry sweep's (INTERNAL_CALL) and carries no view. The secret is never laid out: the hashes
329
+ * are backend-only, and `secretPrefix` is the part a person uses to tell which credential a log line names.
330
+ */
331
+ [R.OAUTH_CLIENTS_ORGANIZATION]: resourceUIBehavior(orgOAuthClientsFactory, OAuthClientOrganizationSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
332
+ icon: KeyRound,
333
+ layout: {
334
+ refs: {
335
+ sections: ['overview', 'access', 'redirects', 'autonomousAgents', 'lifecycle'],
336
+ },
337
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
338
+ // The identity line: the client's name (the page title) and its status beside it. Neither is repeated below.
339
+ header: () => (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-field", children: [_jsx(ResourceHeaderTitle, {}), _jsx(DisplayField, { name: "status", hideLabel: true, hideDescription: true, className: "w-auto" })] })),
340
+ display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "_id" }), _jsx(DisplayField, { name: "secretPrefix" }), _jsx(DisplayField, { name: "description" })] }), _jsxs(Section, { name: "access", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "allowedGrants" }), _jsx(DisplayField, { name: "allowedScopes" }), _jsx(DisplayField, { name: "roles" }), _jsx(DisplayField, { name: "tokenEndpointAuthMethod" }), _jsx(DisplayField, { name: "requiresConsent" })] }), _jsxs(Section, { name: "redirects", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "redirectUris" }), _jsx(DisplayField, { name: "postLogoutRedirectUris" })] }), _jsx(Section, { name: "autonomousAgents", appearance: SectionMode.CARD, children: _jsx(DisplayField, { name: "autonomousAgentRefs" }) }), _jsxs(Section, { name: "lifecycle", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "previousSecretPrefix" }), _jsx(DisplayField, { name: "previousSecretInvalidationDate" }), _jsx(DisplayField, { name: "createdAt" })] })] })),
341
+ summary: () => (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "secretPrefix" }), _jsx(DisplayField, { name: "autonomousAgentRefs" })] })] })),
342
+ edit: OrgOAuthClientEditTemplate,
343
+ },
344
+ fields: {
345
+ // The application client's states and colours, because they mean the same things.
346
+ status: schemaShape.status.enumUI({
347
+ display: { displayMode: EnumDisplayMode.BADGE },
348
+ edit: { showDescription: true },
349
+ values: {
350
+ [OAuthClientStatus.ACTIVE]: { color: BadgeSemanticVariant.SUCCESS },
351
+ [OAuthClientStatus.INACTIVE]: { color: BadgeSemanticVariant.SECONDARY },
352
+ [OAuthClientStatus.EXPIRED]: { color: BadgeSemanticVariant.WARNING },
353
+ },
354
+ }),
355
+ // The prefix keeps its label on a card — bare, it reads as a second name.
356
+ secretPrefix: schemaShape.secretPrefix.stringUI({ summaryOverride: { chromeMode: DisplayChromeMode.INLINE } }),
357
+ description: schemaShape.description.stringUI({
358
+ display: { hideWhenEmpty: true },
359
+ edit: { editMode: StringEditMode.TEXTAREA },
360
+ }),
361
+ postLogoutRedirectUris: schemaShape.postLogoutRedirectUris.arrayUI({ display: { hideWhenEmpty: true } }),
362
+ // On a card only when the client runs an agent: most clients run none, and "Autonomous agents: —" on every
363
+ // card said nothing.
364
+ autonomousAgentRefs: schemaShape.autonomousAgentRefs.arrayUI({
365
+ summaryOverride: { chromeMode: DisplayChromeMode.INLINE, hideWhenEmpty: true },
366
+ }),
367
+ expiresAt: schemaShape.expiresAt.dateUI({ display: { hideWhenEmpty: true } }),
368
+ lastUsedAt: schemaShape.lastUsedAt.dateUI({ display: { hideWhenEmpty: true } }),
369
+ // The rotation window, shown only while the previous secret still authenticates.
370
+ previousSecretPrefix: schemaShape.previousSecretPrefix.stringUI({ display: { hideWhenEmpty: true, showDescription: true } }),
371
+ previousSecretInvalidationDate: schemaShape.previousSecretInvalidationDate.dateUI({ display: { hideWhenEmpty: true } }),
372
+ },
373
+ getFrontendResourceLabel: ({ currentObject }) => currentObject.name,
374
+ views: [
375
+ [Op.READ, notAddressable()],
376
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
377
+ // Creating a client returns its one-time `plainSecret` (`.isEphemeral()`), shown once by the reveal dialog.
378
+ [Op.CREATE, notAddressable()],
379
+ [Op.SEARCH, notAddressable()],
380
+ [Op.DELETE, notAddressable()],
381
+ [OrgOAuthClients_Operations.ROTATE, notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
382
+ [OrgOAuthClients_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
383
+ [OrgOAuthClients_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
384
+ [OrgOAuthClients_Operations.REACTIVATE, notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
385
+ [OrgOAuthClients_Operations.SET_AUTONOMOUS_AGENTS, notAddressable({ iconOverride: Bot, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
386
+ ],
387
+ })),
84
388
  [R.ORG_API_KEYS]: resourceUIBehavior(orgApiKeysFactory, ApiKeyOrganizationSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
85
389
  icon: Key,
86
390
  layout: {
87
391
  refs: {
88
- sections: ['overview', 'access', 'lifecycle'],
392
+ sections: ['overview', 'lifecycle'],
89
393
  },
90
- // Settings-page chrome (Settings Hub → API keys): label-left / value-right rows.
91
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
394
+ // Settings rows on the read page (Settings Hub → API keys), bare values on a card, and a stacked
395
+ // create form: a key is authored once — a name, what it is for, what it may do — not tuned row by row.
396
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
397
+ header: OrgApiKeyHeader,
92
398
  display: OrgApiKeyDisplayTemplate,
93
399
  summary: OrgApiKeySummaryTemplate,
94
400
  edit: OrgApiKeyEditTemplate,
95
401
  },
96
402
  fields: {
403
+ // A badge with no sentence under it. "Determines whether the key can authenticate traffic." printed
404
+ // under every card's status is the field's EDIT help — the form keeps it.
97
405
  status: schemaShape.status.enumUI({
98
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
406
+ display: { displayMode: EnumDisplayMode.BADGE },
99
407
  edit: { showDescription: true },
100
408
  values: {
101
409
  [ApiKeyStatus.ACTIVE]: { color: BadgeSemanticVariant.SUCCESS },
@@ -103,27 +411,35 @@ export function buildSecurityMainPresets() {
103
411
  [ApiKeyStatus.EXPIRED]: { color: BadgeSemanticVariant.WARNING },
104
412
  },
105
413
  }),
414
+ // The one card value that keeps its label: bare, a prefix reads as a second name — see the summary.
415
+ keyPrefix: schemaShape.keyPrefix.stringUI({ summaryOverride: { chromeMode: DisplayChromeMode.INLINE } }),
416
+ description: schemaShape.description.stringUI({
417
+ display: { hideWhenEmpty: true },
418
+ edit: { editMode: StringEditMode.TEXTAREA },
419
+ }),
420
+ // A key with no expiry, or one never used, is ordinary — a dash on those rows said nothing.
421
+ expiresAt: schemaShape.expiresAt.dateUI({ display: { hideWhenEmpty: true } }),
422
+ lastUsedAt: schemaShape.lastUsedAt.dateUI({ display: { hideWhenEmpty: true } }),
423
+ // The rotation window, rendered only while the previous secret still authenticates (`ApiKeyRotationWindowRows`).
424
+ // The prefix row says what an empty end date means — after a regenerate the old secret has none and keeps
425
+ // working — so it carries its description; the end-date row hides itself when there is no date.
426
+ previousKeyPrefix: schemaShape.previousKeyPrefix.stringUI({ display: { hideWhenEmpty: true, showDescription: true } }),
427
+ previousKeyInvalidationDate: schemaShape.previousKeyInvalidationDate.dateUI({ display: { hideWhenEmpty: true } }),
106
428
  },
107
429
  getFrontendResourceLabel: ({ currentObject }) => currentObject.name,
108
430
  // The empty state's primary action is Create — surfaced because CREATE
109
431
  // declares a (non-addressable, overlay) view below. Creating a key returns
110
432
  // its one-time `plainKey` (`.isEphemeral()` on the CREATE response DTO) →
111
433
  // the ephemeral reveal dialog shows the secret exactly once.
112
- emptyState: { primaryAction: Op.CREATE },
113
434
  views: [
114
435
  [Op.READ, notAddressable()],
115
- // A key registry: name, scope, created, last used. Comparable rows.
436
+ // A key registry: name, status, prefix, expiry, last use. Comparable rows.
116
437
  // CARDS is the framework default and nothing had ever declared otherwise, so every
117
438
  // collection in the product rendered as stacked cards — ~150px per row, four people to a
118
439
  // viewport, with each field's help text and label repeated on every card. TABLE writes the
119
440
  // headers once. `userSwitchableDisplayMode` keeps cards one click away rather than removing
120
441
  // a rendering somebody may prefer on a narrow screen.
121
- [Op.LIST, addressable({
122
- collectionDisplayConfig: {
123
- displayMode: CollectionDisplayMode.TABLE,
124
- userSwitchableDisplayMode: true,
125
- },
126
- })],
442
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
127
443
  // CREATE has a view but is NOT URL-addressable — surface EMBEDDED; the create
128
444
  // form opens as a drill-in within the Settings Hub panel, not a routed page.
129
445
  // Its Create button still shows: action visibility is governed by the op
@@ -131,11 +447,11 @@ export function buildSecurityMainPresets() {
131
447
  [Op.CREATE, notAddressable()],
132
448
  [Op.SEARCH, notAddressable()],
133
449
  [Op.DELETE, notAddressable()],
134
- [OrgApiKeys_Operations.ROTATE, notAddressable({ iconOverride: RotateCw })],
135
- [OrgApiKeys_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw })],
136
- [OrgApiKeys_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff })],
137
- [OrgApiKeys_Operations.REACTIVATE, notAddressable({ iconOverride: Power })],
138
- [OrgApiKeys_Operations.EXTEND_EXPIRY, notAddressable({ iconOverride: CalendarPlus })],
450
+ [OrgApiKeys_Operations.ROTATE, notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
451
+ [OrgApiKeys_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
452
+ [OrgApiKeys_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
453
+ [OrgApiKeys_Operations.REACTIVATE, notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
454
+ [OrgApiKeys_Operations.EXTEND_EXPIRY, notAddressable({ iconOverride: CalendarPlus, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
139
455
  [OrgApiKeys_Operations.USAGE_STATS, notAddressable({ iconOverride: ChartColumn })],
140
456
  ],
141
457
  })),
@@ -150,8 +466,8 @@ export function buildSecurityMainPresets() {
150
466
  * ## No layout refs, deliberately
151
467
  *
152
468
  * Declaring `layout.refs.sections` creates a contract the resource SPECIFICATION has to document
153
- * slot for slot, validated at load; this resource has eight visible fields and no section would
154
- * earn that coupling. Field-level UI is the whole surface until it needs one.
469
+ * slot for slot, validated at load; this resource has a handful of visible facts and no section would
470
+ * earn that coupling. Its templates, shared with the person-scope sibling, are flat rows under a header.
155
471
  *
156
472
  * ## What the administrator can see, and what they cannot
157
473
  *
@@ -161,9 +477,19 @@ export function buildSecurityMainPresets() {
161
477
  */
162
478
  [R.ORG_PROVIDER_CREDENTIALS]: resourceUIBehavior(orgProviderCredentialsFactory, OrganizationProviderCredentialSchema, ({ schemaShape }) => ({
163
479
  icon: KeyRound,
480
+ layout: {
481
+ defaultChromeMode: providerCredentialPresentation(schemaShape).defaultChromeMode,
482
+ header: ProviderCredentialHeader,
483
+ display: providerCredentialDisplayTemplate(true),
484
+ summary: ProviderCredentialSummaryTemplate,
485
+ edit: ProviderCredentialEditTemplate,
486
+ },
164
487
  fields: {
488
+ ...providerCredentialPresentation(schemaShape).fields,
489
+ // Absent for a credential the system stored rather than a person — a dash there said nothing.
490
+ setByUserId: schemaShape.setByUserId.stringUI({ display: { hideWhenEmpty: true } }),
165
491
  validationState: schemaShape.validationState.enumUI({
166
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
492
+ display: { displayMode: EnumDisplayMode.BADGE },
167
493
  edit: { showDescription: true },
168
494
  values: {
169
495
  // UNVALIDATED is the honest default and is not a warning: storing a credential proves
@@ -192,13 +518,21 @@ export function buildSecurityMainPresets() {
192
518
  })),
193
519
  [R.USER_PROVIDER_CREDENTIALS]: resourceUIBehavior(userProviderCredentialsFactory, UserProviderCredentialSchema, ({ schemaShape }) => ({
194
520
  icon: KeyRound,
521
+ layout: {
522
+ defaultChromeMode: providerCredentialPresentation(schemaShape).defaultChromeMode,
523
+ header: ProviderCredentialHeader,
524
+ display: providerCredentialDisplayTemplate(false),
525
+ summary: ProviderCredentialSummaryTemplate,
526
+ edit: ProviderCredentialEditTemplate,
527
+ },
195
528
  fields: {
529
+ ...providerCredentialPresentation(schemaShape).fields,
196
530
  // The same three states as the tenant's, coloured identically on purpose: a person reading
197
531
  // their own connections and an administrator reading the tenant's are answering the same
198
532
  // question, and two colour languages for one vocabulary is how a reader learns to distrust
199
533
  // both. The WORDS come from the enum's i18n entry, authored once for every surface.
200
534
  validationState: schemaShape.validationState.enumUI({
201
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
535
+ display: { displayMode: EnumDisplayMode.BADGE },
202
536
  edit: { showDescription: true },
203
537
  values: {
204
538
  // UNVALIDATED is the honest default and is not a warning: storing a credential proves
@@ -218,9 +552,9 @@ export function buildSecurityMainPresets() {
218
552
  // CREATE form took both as free text, so a value could be stored in a slot nothing reads, and the refusal naming
219
553
  // the valid slots never reached the person. The API's CREATE stays for callers that are not these screens.
220
554
  emptyState: { primaryAction: false },
221
- // No `layout.refs`, matching the tenant sibling: this resource declares no layout sections in
222
- // its specification, and a preset naming refs the layout does not declare fails the
223
- // structural-slot validator at import time and crash-loops the companion.
555
+ // No `layout.refs`, matching the tenant sibling: the templates use no `<Section>`, this resource
556
+ // declares no layout sections in its specification, and a preset naming refs the specification does
557
+ // not document fails the structural-slot validator at import time and crash-loops the companion.
224
558
  views: [
225
559
  [Op.READ, notAddressable()],
226
560
  [Op.LIST, addressable()],
@@ -231,14 +565,17 @@ export function buildSecurityMainPresets() {
231
565
  })),
232
566
  [R.AUDIT_LOGS]: resourceUIBehavior(auditLogsFactory, AuditableEvent_BaseSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
233
567
  icon: ShieldCheck,
234
- getFrontendResourceLabel: ({ currentObject }) => `${currentObject.eventType ?? 'Audit Event'} — ${currentObject.createdAt?.toISOString?.() ?? ''}`,
568
+ // An audit event is named by its type's word and the instant it happened — see `auditEventName`.
569
+ getFrontendResourceLabel: auditEventName,
235
570
  layout: {
236
571
  refs: {
237
572
  sections: ['overview', 'actors', 'payload'],
238
573
  },
574
+ defaultChromeMode: auditLogPresentation(schemaShape).defaultChromeMode,
239
575
  display: AuditLogDisplayTemplate,
240
576
  summary: AuditLogSummaryTemplate,
241
577
  },
578
+ fields: auditLogPresentation(schemaShape).fields,
242
579
  views: [
243
580
  [Op.READ, notAddressable()],
244
581
  // Audit-trail review dashboard: severity quick tabs (All/Info/Warning/…)
@@ -269,8 +606,8 @@ export function buildSecurityMainPresets() {
269
606
  // `display` and `summary` joined `edit` on 2026-09-18. Only the form carried the settings
270
607
  // treatment, so the READ an operator actually opens rendered with the stacked default while
271
608
  // its own edit form used rows — the same record, two shapes, depending on which verb you
272
- // reached it by.
273
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
609
+ // reached it by. The card is bare: its one value reads as the word ("Enabled"), see `enabled`.
610
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
274
611
  // SIEM export configuration — a scope-level configuration record. A settings pane is a list of decisions an operator came to TAKE, so its
275
612
  // writable fields are offered in place rather than behind a whole-pane edit mode. The set is
276
613
  // DERIVED (not primary key, not excludeFromUpdate, not hidden, not opted out), so a field
@@ -283,12 +620,11 @@ export function buildSecurityMainPresets() {
283
620
  // refuses to answer.
284
621
  defaultEmptyValue: 'placeholder',
285
622
  edit: () => {
286
- const { isExisting, operationType } = useFormTemplateContext();
287
- return (_jsxs(FormLayout, { children: [_jsx(Section, { name: "status", appearance: SectionMode.CARD, chromeMode: EditChromeMode.INLINE, children: _jsx(FormField, { name: "enabled" }) }), _jsx(Section, { name: "destination", appearance: SectionMode.CARD, children: _jsx(FormField, { name: "webhookUrl" }) }), _jsxs(Section, { name: "authentication", children: [_jsx(FormField, { name: "authMethod" }), _jsx(FormField, { name: "authCredential" }), _jsx(FormField, { name: "authHeaderName" })] }), _jsxs(Section, { name: "format", children: [_jsx(FormField, { name: "eventFormat" }), _jsxs(ObjectScope, { name: "eventFilter", children: [_jsx(FormField, { name: "categories" }), _jsx(FormField, { name: "severityMinimum" }), _jsx(FormField, { name: "specificEventTypes" })] }), _jsxs(ObjectScope, { name: "exportProjection", children: [_jsx(FormField, { name: "disclosedTiers" }), _jsx(FormField, { name: "disclosedEventDataClasses" })] })] }), _jsxs(Section, { name: "advanced", appearance: SectionMode.COLLAPSIBLE, defaultFocus: false, children: [_jsxs(ObjectScope, { name: "delivery", children: [_jsx(FormField, { name: "retryAttempts" }), _jsx(FormField, { name: "retryBackoffSeconds" }), _jsx(FormField, { name: "timeoutSeconds" })] }), _jsx(FormField, { name: "customHeaders" })] })] }));
623
+ return (_jsxs(FormLayout, { children: [_jsx(Section, { name: "status", appearance: SectionMode.CARD, chromeMode: EditChromeMode.INLINE, hideTitle: true, children: _jsx(FormField, { name: "enabled" }) }), _jsx(Section, { name: "destination", appearance: SectionMode.CARD, hideTitle: true, children: _jsx(FormField, { name: "webhookUrl" }) }), _jsxs(Section, { name: "authentication", children: [_jsx(FormField, { name: "authMethod" }), _jsx(FormField, { name: "authCredential" }), _jsx(FormField, { name: "authHeaderName" })] }), _jsxs(Section, { name: "format", children: [_jsx(FormField, { name: "eventFormat" }), _jsxs(ObjectScope, { name: "eventFilter", children: [_jsx(FormField, { name: "categories" }), _jsx(FormField, { name: "severityMinimum" }), _jsx(FormField, { name: "specificEventTypes" })] }), _jsxs(ObjectScope, { name: "exportProjection", children: [_jsx(FormField, { name: "disclosedTiers" }), _jsx(FormField, { name: "disclosedEventDataClasses" })] })] }), _jsxs(Section, { name: "advanced", appearance: SectionMode.COLLAPSIBLE, defaultFocus: false, children: [_jsxs(ObjectScope, { name: "delivery", children: [_jsx(FormField, { name: "retryAttempts" }), _jsx(FormField, { name: "retryBackoffSeconds" }), _jsx(FormField, { name: "timeoutSeconds" })] }), _jsx(FormField, { name: "customHeaders" })] })] }));
288
624
  },
289
625
  display: () => {
290
626
  const { data, isLoading } = useDisplayTemplateContext();
291
- return (_jsxs(DisplayLayout, { children: [_jsx(Section, { name: "status", appearance: SectionMode.CARD, children: _jsx(DisplayField, { name: "enabled" }) }), _jsx(Section, { name: "destination", appearance: SectionMode.CARD, children: _jsx(DisplayField, { name: "webhookUrl" }) }), _jsxs(Section, { name: "authentication", children: [_jsx(DisplayField, { name: "authMethod" }), _jsx(DisplayField, { name: "authCredential" }), _jsx(DisplayField, { name: "authHeaderName" })] }), _jsxs(Section, { name: "format", children: [_jsx(DisplayField, { name: "eventFormat" }), _jsxs(ObjectScope, { name: "eventFilter", children: [_jsx(DisplayField, { name: "categories" }), _jsx(DisplayField, { name: "severityMinimum" }), _jsx(DisplayField, { name: "specificEventTypes" })] }), _jsxs(ObjectScope, { name: "exportProjection", children: [_jsx(DisplayField, { name: "disclosedTiers" }), _jsx(DisplayField, { name: "disclosedEventDataClasses" })] })] }), _jsxs(Section, { name: "advanced", appearance: SectionMode.COLLAPSIBLE, defaultFocus: false, children: [_jsxs(ObjectScope, { name: "delivery", children: [_jsx(DisplayField, { name: "retryAttempts" }), _jsx(DisplayField, { name: "retryBackoffSeconds" }), _jsx(DisplayField, { name: "timeoutSeconds" })] }), _jsx(DisplayField, { name: "customHeaders" })] })] }));
627
+ return (_jsxs(DisplayLayout, { children: [_jsx(Section, { name: "status", appearance: SectionMode.CARD, hideTitle: true, children: _jsx(DisplayField, { name: "enabled" }) }), _jsx(Section, { name: "destination", appearance: SectionMode.CARD, hideTitle: true, children: _jsx(DisplayField, { name: "webhookUrl" }) }), _jsxs(Section, { name: "authentication", hideDescription: true, children: [_jsx(DisplayField, { name: "authMethod" }), _jsx(DisplayField, { name: "authCredential" }), _jsx(DisplayField, { name: "authHeaderName" })] }), _jsxs(Section, { name: "format", hideDescription: true, children: [_jsx(DisplayField, { name: "eventFormat" }), _jsxs(ObjectScope, { name: "eventFilter", children: [_jsx(DisplayField, { name: "categories" }), _jsx(DisplayField, { name: "severityMinimum" }), _jsx(DisplayField, { name: "specificEventTypes" })] }), _jsxs(ObjectScope, { name: "exportProjection", children: [_jsx(DisplayField, { name: "disclosedTiers" }), _jsx(DisplayField, { name: "disclosedEventDataClasses" })] })] }), _jsxs(Section, { name: "advanced", appearance: SectionMode.COLLAPSIBLE, defaultFocus: false, hideDescription: true, children: [_jsxs(ObjectScope, { name: "delivery", children: [_jsx(DisplayField, { name: "retryAttempts" }), _jsx(DisplayField, { name: "retryBackoffSeconds" }), _jsx(DisplayField, { name: "timeoutSeconds" })] }), _jsx(DisplayField, { name: "customHeaders" })] })] }));
292
628
  },
293
629
  summary: SiemExportSummaryTemplate,
294
630
  },
@@ -319,15 +655,18 @@ export function buildSecurityMainPresets() {
319
655
  icon: AlertTriangle,
320
656
  layout: {
321
657
  refs: {
322
- sections: ['overview', 'delivery', 'payload'],
658
+ sections: ['delivery', 'payload'],
323
659
  },
660
+ // Settings rows on read, bare values on a card: the card is the failure reason and its status badge.
661
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
662
+ header: SiemDlqHeader,
324
663
  display: SiemDlqDisplayTemplate,
325
664
  summary: SiemDlqSummaryTemplate,
326
665
  },
327
666
  fields: {
328
- // Same billing-style visibility as ORG_API_KEYS / SSO protocol — list rows stay compact; descriptions come from schema metadata when present.
667
+ // A badge with no sentence under it on read; the enum's description is edit help.
329
668
  status: schema.shape.status.enumUI({
330
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
669
+ display: { displayMode: EnumDisplayMode.BADGE },
331
670
  edit: { showDescription: true },
332
671
  values: {
333
672
  [SiemDeliveryDlqStatus.PENDING_RETRY]: { color: BadgeSemanticVariant.WARNING },
@@ -335,6 +674,8 @@ export function buildSecurityMainPresets() {
335
674
  [SiemDeliveryDlqStatus.DELIVERED]: { color: BadgeSemanticVariant.SUCCESS },
336
675
  },
337
676
  }),
677
+ // Empty once the row is terminal (exhausted or delivered) — a dash there said nothing.
678
+ nextRetryAt: schema.shape.nextRetryAt.dateUI({ display: { hideWhenEmpty: true } }),
338
679
  },
339
680
  getFrontendResourceLabel: ({ currentObject }) => currentObject.failureReason,
340
681
  views: [
@@ -365,62 +706,84 @@ export function buildSecurityMainPresets() {
365
706
  { parentResourceType: R.ORGANIZATIONS, resourceType: R.ORGANIZATION_SSO_CONFIG },
366
707
  { parentResourceType: R.APPLICATION, resourceType: R.APPLICATION_SSO_CONFIG },
367
708
  ],
368
- }, ssoConfigPolymorphicFactories[R.ORGANIZATION_SSO_CONFIG], SsoConfigBaseSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
369
- icon: StandardIconReference.LOCK,
370
- layout: {
371
- // Settings-page chrome (Settings Hub): label-left / value-right rows. A scope-level
372
- // configuration record is READ far more often than it is edited — an operator opens it to
373
- // confirm a value — so the DISPLAY side needs the row treatment at least as much as the form.
374
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
375
- // SSO policy — a scope-level configuration record; every field here is a decision. A settings pane is a list of decisions an operator came to TAKE, so its
376
- // writable fields are offered in place rather than behind a whole-pane edit mode. The set is
377
- // DERIVED (not primary key, not excludeFromUpdate, not hidden, not opted out), so a field
378
- // added to this schema tomorrow is covered without anyone remembering to decorate it.
379
- defaultInlineEdit: ResourceLayoutInlineEditDefault.WRITABLE_FIELDS,
380
- // And its empty rows say WHICH emptiness they are — "None yet" for a list with no members,
381
- // "Not available" for a computed value, "No value" for a field awaiting input — instead of one
382
- // em dash standing for all three. The surface policy's dash is right where a reader is scanning;
383
- // on a settings row, which is read one at a time to decide something, it is an answer that
384
- // refuses to answer.
385
- defaultEmptyValue: 'placeholder',
386
- // Seven groups of decisions — enforcement, domains, provisioning, session, audit,
387
- // emergency access, OAuth restrictions — and thirty fields between them. Stacked, the pane
388
- // is a scroll in which the thing you came to change is below the fold and the six you did
389
- // not is what you read on the way. Vertical tabs is what a settings surface of this size
390
- // does: one group at a time, all of them named and reachable in one click, and the rail
391
- // doubles as the answer to "what else does SSO decide here".
392
- //
393
- // Declared on the LAYOUT rather than on the seven sections: the section grouping keys on
394
- // `(mode, its mode-specific props)` over consecutive siblings, so per-section it is an
395
- // agreement N authors must keep, and a member that slips renders a second rail rather than
396
- // failing. It is also what lets the Settings Hub know the pane carries a rail of its own
397
- // and give it the wider frame — a prop inside a React template cannot be read by anything
398
- // that has to size the surface around it.
399
- defaultDisplaySectionAppearance: SectionMode.VERTICAL_TABS,
400
- refs: {
401
- sections: ['enforcement', 'domains', 'session', 'emergency', 'oauth'],
709
+ }, ssoConfigPolymorphicFactories[R.ORGANIZATION_SSO_CONFIG], SsoConfigBaseSchema, ({ schema, schemaShape, opsVariantResolver }) => {
710
+ /*
711
+ * The one sentence on this pane that is a warning rather than a restatement: each break-glass address is a
712
+ * standing way in with a password alone, so the list is kept short and reviewed. It belongs to the field it
713
+ * warns about, on the row an administrator edits in place and in the form — the pane's vertical tabs render
714
+ * no section description. Its words are the `whyItMatters` the SSO policy specification declares on this
715
+ * nested leaf (the labeller reads a nested field's spec under its dotted path). Without that declaration the
716
+ * generated description restated the label ("Break-glass accounts" under "Break-glass accounts").
717
+ * spec-cite: N/A — the quoted words are this application's own generated label, and "specification" is our
718
+ * SSO policy resource specification, not an external standard.
719
+ *
720
+ * Decorated here rather than through `fields`, whose keys are top-level: a decorator writes the nested
721
+ * declaration's own metadata in place, which is what `fields` relies on for top-level fields too.
722
+ */
723
+ schema.shape.emergencyAccess.shape.breakGlassAccounts.arrayUI({
724
+ display: { showDescription: true },
725
+ edit: { showDescription: true },
726
+ });
727
+ return {
728
+ icon: StandardIconReference.LOCK,
729
+ layout: {
730
+ // Settings-page chrome (Settings Hub): label-left / value-right rows. A scope-level
731
+ // configuration record is READ far more often than it is edited — an operator opens it to
732
+ // confirm a value — so the DISPLAY side needs the row treatment at least as much as the form.
733
+ // The card stays INLINE on purpose: it is ONE boolean (`enforcement.requireSSO`), and a bare checkmark
734
+ // says nothing about what is required. The value is nested, so a field-level `summaryOverride` cannot
735
+ // reach it — the layout default is the only place to keep its label.
736
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
737
+ // SSO policy — a scope-level configuration record; every field here is a decision. A settings pane is a list of decisions an operator came to TAKE, so its
738
+ // writable fields are offered in place rather than behind a whole-pane edit mode. The set is
739
+ // DERIVED (not primary key, not excludeFromUpdate, not hidden, not opted out), so a field
740
+ // added to this schema tomorrow is covered without anyone remembering to decorate it.
741
+ defaultInlineEdit: ResourceLayoutInlineEditDefault.WRITABLE_FIELDS,
742
+ // And its empty rows say WHICH emptiness they are — "None yet" for a list with no members,
743
+ // "Not available" for a computed value, "No value" for a field awaiting input — instead of one
744
+ // em dash standing for all three. The surface policy's dash is right where a reader is scanning;
745
+ // on a settings row, which is read one at a time to decide something, it is an answer that
746
+ // refuses to answer.
747
+ defaultEmptyValue: 'placeholder',
748
+ // Seven groups of decisions — enforcement, domains, provisioning, session, audit,
749
+ // emergency access, OAuth restrictions — and thirty fields between them. Stacked, the pane
750
+ // is a scroll in which the thing you came to change is below the fold and the six you did
751
+ // not is what you read on the way. Vertical tabs is what a settings surface of this size
752
+ // does: one group at a time, all of them named and reachable in one click, and the rail
753
+ // doubles as the answer to "what else does SSO decide here".
754
+ //
755
+ // Declared on the LAYOUT rather than on the seven sections: the section grouping keys on
756
+ // `(mode, its mode-specific props)` over consecutive siblings, so per-section it is an
757
+ // agreement N authors must keep, and a member that slips renders a second rail rather than
758
+ // failing. It is also what lets the Settings Hub know the pane carries a rail of its own
759
+ // and give it the wider frame — a prop inside a React template cannot be read by anything
760
+ // that has to size the surface around it.
761
+ defaultDisplaySectionAppearance: SectionMode.VERTICAL_TABS,
762
+ refs: {
763
+ sections: ['enforcement', 'domains', 'session', 'emergency', 'oauth'],
764
+ },
765
+ display: SsoConfigDisplayTemplate,
766
+ summary: SsoConfigSummaryTemplate,
402
767
  },
403
- display: SsoConfigDisplayTemplate,
404
- summary: SsoConfigSummaryTemplate,
405
- },
406
- views: [
407
- [Op.READ, addressable()],
408
- // The edit an administrator opens this pane to make, which until now nothing could save: the
409
- // literal UPDATE was INTERNAL_CALL, so no route existed and the frontend's update resolution
410
- // fell through to whichever custom verb below borrowed UPDATE semantics. Not addressable — it
411
- // opens from the READ view, like SIEM export config's (#953).
412
- [Op.UPDATE, notAddressable()],
413
- // Domain ownership is the one publicly mutable part of this policy row (#424). The three
414
- // actions live beside the `domains` section: claim returns a DNS record to copy, verify
415
- // resolves it, revoke releases the domain for another scope to verify.
416
- [SsoConfig_Operations.CLAIM_DOMAIN, notAddressable({ iconOverride: Globe })],
417
- [SsoConfig_Operations.VERIFY_DOMAIN, notAddressable({ iconOverride: ShieldCheck })],
418
- [SsoConfig_Operations.REVOKE_DOMAIN, notAddressable({ iconOverride: Unlink })],
419
- ],
420
- variantOverrides: {
421
- [R.APPLICATION]: { views: [] },
422
- },
423
- })),
768
+ views: [
769
+ [Op.READ, addressable()],
770
+ // The edit an administrator opens this pane to make, which until now nothing could save: the
771
+ // literal UPDATE was INTERNAL_CALL, so no route existed and the frontend's update resolution
772
+ // fell through to whichever custom verb below borrowed UPDATE semantics. Not addressable — it
773
+ // opens from the READ view, like SIEM export config's (#953).
774
+ [Op.UPDATE, notAddressable()],
775
+ // Domain ownership is the one publicly mutable part of this policy row (#424). The three
776
+ // actions live beside the `domains` section: claim returns a DNS record to copy, verify
777
+ // resolves it, revoke releases the domain for another scope to verify.
778
+ [SsoConfig_Operations.CLAIM_DOMAIN, notAddressable({ iconOverride: Globe })],
779
+ [SsoConfig_Operations.VERIFY_DOMAIN, notAddressable({ iconOverride: ShieldCheck })],
780
+ [SsoConfig_Operations.REVOKE_DOMAIN, notAddressable({ iconOverride: Unlink })],
781
+ ],
782
+ variantOverrides: {
783
+ [R.APPLICATION]: { views: [] },
784
+ },
785
+ };
786
+ }),
424
787
  ...polymorphicResourceUIBehavior({
425
788
  variants: [
426
789
  { parentResourceType: R.ORGANIZATIONS, resourceType: R.ORGANIZATION_SSO_CONNECTIONS },
@@ -432,18 +795,30 @@ export function buildSecurityMainPresets() {
432
795
  refs: {
433
796
  sections: ['overview', 'oidc', 'saml', 'provisioning'],
434
797
  },
798
+ // Settings rows on read, bare values on a card (the name, the protocol badge, on/off as the word).
799
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
800
+ header: SsoConnectionHeader,
435
801
  display: SsoConnectionDisplayTemplate,
436
802
  summary: SsoConnectionSummaryTemplate,
437
803
  },
438
804
  fields: {
805
+ // A badge with no sentence under it on read ("Selects the authentication protocol…" sat under every
806
+ // card); the enum's description is edit help.
439
807
  protocol: schema.shape.protocol.enumUI({
440
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
808
+ display: { displayMode: EnumDisplayMode.BADGE },
441
809
  edit: { showDescription: true },
442
810
  values: {
443
811
  [SsoProtocol.OIDC]: { color: BadgeSemanticVariant.INFO },
444
812
  [SsoProtocol.SAML]: { color: BadgeSemanticVariant.SECONDARY },
445
813
  },
446
814
  }),
815
+ // On a card the value reads bare, and a bare checkmark says nothing about WHAT is true — so the card
816
+ // says the word ("Enabled" / "Disabled"); the read page keeps the labelled row and its checkmark.
817
+ enabled: schema.shape.enabled.booleanUI({
818
+ labellingValue: BooleanLabellingValue.ENABLED_DISABLED,
819
+ summaryOverride: { displayMode: BooleanDisplayMode.RAW },
820
+ }),
821
+ externalProviderId: schema.shape.externalProviderId.stringUI({ display: { hideWhenEmpty: true } }),
447
822
  },
448
823
  getFrontendResourceLabel: ({ currentObject }) => currentObject.displayName,
449
824
  views: [
@@ -453,12 +828,7 @@ export function buildSecurityMainPresets() {
453
828
  // viewport, with each field's help text and label repeated on every card. TABLE writes the
454
829
  // headers once. `userSwitchableDisplayMode` keeps cards one click away rather than removing
455
830
  // a rendering somebody may prefer on a narrow screen.
456
- [Op.LIST, addressable({
457
- collectionDisplayConfig: {
458
- displayMode: CollectionDisplayMode.TABLE,
459
- userSwitchableDisplayMode: true,
460
- },
461
- })],
831
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
462
832
  [SsoConnection_Operations.TEST_CONNECTION, addressable({
463
833
  iconOverride: PlugZap,
464
834
  customView: ResourceLayoutPreset_Read_SsoSaml,
@@ -483,7 +853,7 @@ export function buildSecurityMainPresets() {
483
853
  // Settings-page chrome (Settings Hub): label-left / value-right rows. A scope-level
484
854
  // configuration record is READ far more often than it is edited — an operator opens it to
485
855
  // confirm a value — so the DISPLAY side needs the row treatment at least as much as the form.
486
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
856
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
487
857
  // SCIM provisioning policy — a scope-level configuration record. A settings pane is a list of decisions an operator came to TAKE, so its
488
858
  // writable fields are offered in place rather than behind a whole-pane edit mode. The set is
489
859
  // DERIVED (not primary key, not excludeFromUpdate, not hidden, not opted out), so a field
@@ -501,6 +871,10 @@ export function buildSecurityMainPresets() {
501
871
  display: ScimProvisioningDisplayTemplate,
502
872
  summary: ScimProvisioningSummaryTemplate,
503
873
  },
874
+ fields: {
875
+ // The card's one value keeps its label: a bare checkmark says nothing about WHAT is switched on.
876
+ autoCreateUsers: schema.shape.autoCreateUsers.booleanUI({ summaryOverride: { chromeMode: DisplayChromeMode.INLINE } }),
877
+ },
504
878
  views: [
505
879
  [Op.READ, addressable()],
506
880
  // The four decisions on this row — create users, verify their email, deactivate on
@@ -524,9 +898,21 @@ export function buildSecurityMainPresets() {
524
898
  refs: {
525
899
  sections: ['overview', 'lifecycle'],
526
900
  },
901
+ // Settings rows on read, bare values on a card (the name, then active or not as the word).
902
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
527
903
  display: ScimTokenDisplayTemplate,
528
904
  summary: ScimTokenSummaryTemplate,
529
905
  },
906
+ fields: {
907
+ // "Active: ✓" on the card becomes the word: a bare checkmark says nothing about what is true.
908
+ isActive: schema.shape.isActive.booleanUI({
909
+ labellingValue: BooleanLabellingValue.ACTIVE_INACTIVE,
910
+ summaryOverride: { displayMode: BooleanDisplayMode.RAW },
911
+ }),
912
+ // A token never used, or with no expiry, is ordinary — a dash on those rows said nothing.
913
+ lastUsedAt: schema.shape.lastUsedAt.dateUI({ display: { hideWhenEmpty: true } }),
914
+ expiresAt: schema.shape.expiresAt.dateUI({ display: { hideWhenEmpty: true } }),
915
+ },
530
916
  views: [
531
917
  // A token registry: name, status, last used, expiry. Comparable rows.
532
918
  // CARDS is the framework default and nothing had ever declared otherwise, so every
@@ -534,12 +920,7 @@ export function buildSecurityMainPresets() {
534
920
  // viewport, with each field's help text and label repeated on every card. TABLE writes the
535
921
  // headers once. `userSwitchableDisplayMode` keeps cards one click away rather than removing
536
922
  // a rendering somebody may prefer on a narrow screen.
537
- [Op.LIST, addressable({
538
- collectionDisplayConfig: {
539
- displayMode: CollectionDisplayMode.TABLE,
540
- userSwitchableDisplayMode: true,
541
- },
542
- })],
923
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
543
924
  [ScimTokens_Operations.GENERATE_TOKEN, notAddressable({ iconOverride: KeyRound })],
544
925
  [ScimTokens_Operations.REVOKE_TOKEN, notAddressable({ iconOverride: ShieldX })],
545
926
  [ScimTokens_Operations.ROTATE_TOKEN, notAddressable({ iconOverride: RotateCw })],
@@ -569,14 +950,17 @@ export function buildSecurityAdminPresets(main) {
569
950
  */
570
951
  [R.APPLICATION_AUDIT_LOGS]: resourceUIBehavior(applicationAuditLogsFactory, AuditableEvent_BaseSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
571
952
  icon: ShieldCheck,
572
- getFrontendResourceLabel: ({ currentObject }) => `${currentObject.eventType ?? 'Audit Event'} — ${currentObject.createdAt?.toISOString?.() ?? ''}`,
953
+ // An audit event is named by its type's word and the instant it happened — see `auditEventName`.
954
+ getFrontendResourceLabel: auditEventName,
573
955
  layout: {
574
956
  refs: {
575
957
  sections: ['overview', 'actors', 'payload'],
576
958
  },
959
+ defaultChromeMode: auditLogPresentation(schemaShape).defaultChromeMode,
577
960
  display: AuditLogDisplayTemplate,
578
961
  summary: AuditLogSummaryTemplate,
579
962
  },
963
+ fields: auditLogPresentation(schemaShape).fields,
580
964
  views: [
581
965
  [Op.READ, notAddressable()],
582
966
  // Same dashboard capabilities as the tenant door — `severity` is in this operation's own
@@ -689,8 +1073,9 @@ export function buildSecurityAdminPresets(main) {
689
1073
  layout: base.layout,
690
1074
  fields: base.fields,
691
1075
  getFrontendResourceLabel: base.getFrontendResourceLabel,
1076
+ // The main lane's registry TABLE, for the reason given on SCIM tokens below.
692
1077
  views: [
693
- [Op.LIST, addressable()],
1078
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
694
1079
  [SsoConnection_Operations.TEST_CONNECTION, addressable({
695
1080
  iconOverride: PlugZap,
696
1081
  customView: ResourceLayoutPreset_Read_SsoSaml,
@@ -704,7 +1089,7 @@ export function buildSecurityAdminPresets(main) {
704
1089
  [R.ORGANIZATIONS]: {
705
1090
  views: [
706
1091
  [Op.READ, notAddressable()],
707
- [Op.LIST, notAddressable()],
1092
+ [Op.LIST, notAddressable({ collectionDisplayConfig: REGISTRY_TABLE })],
708
1093
  ],
709
1094
  },
710
1095
  },
@@ -717,6 +1102,7 @@ export function buildSecurityAdminPresets(main) {
717
1102
  }, scimProvisioningConfigPolymorphicFactories[R.ORGANIZATION_SCIM_PROVISIONING_CONFIG], main, ({ base }) => ({
718
1103
  icon: base.icon,
719
1104
  layout: base.layout,
1105
+ fields: base.fields,
720
1106
  views: [
721
1107
  [Op.READ, addressable()],
722
1108
  // The admin app's own edit door for the APP-scope row. The `main` preset above declares
@@ -738,9 +1124,12 @@ export function buildSecurityAdminPresets(main) {
738
1124
  }, scimTokensPolymorphicFactories[R.ORGANIZATION_SCIM_TOKENS], main, ({ base }) => ({
739
1125
  icon: base.icon,
740
1126
  layout: base.layout,
1127
+ fields: base.fields,
741
1128
  getFrontendResourceLabel: base.getFrontendResourceLabel,
1129
+ // The same registry TABLE the main lane declares: this lane REPLACES the main entry in an application
1130
+ // that composes both scopes, so leaving it out rendered the inventory as stacked cards there.
742
1131
  views: [
743
- [Op.LIST, addressable()],
1132
+ [Op.LIST, addressable({ collectionDisplayConfig: REGISTRY_TABLE })],
744
1133
  [ScimTokens_Operations.GENERATE_TOKEN, notAddressable({ iconOverride: KeyRound })],
745
1134
  [ScimTokens_Operations.REVOKE_TOKEN, notAddressable({ iconOverride: ShieldX })],
746
1135
  [ScimTokens_Operations.ROTATE_TOKEN, notAddressable({ iconOverride: RotateCw })],
@@ -749,28 +1138,67 @@ export function buildSecurityAdminPresets(main) {
749
1138
  [R.ORGANIZATIONS]: {
750
1139
  views: [
751
1140
  [Op.READ, notAddressable()],
752
- [Op.LIST, notAddressable()],
1141
+ [Op.LIST, notAddressable({ collectionDisplayConfig: REGISTRY_TABLE })],
753
1142
  ],
754
1143
  },
755
1144
  },
756
1145
  })),
1146
+ /**
1147
+ * The application operator's lane onto an organization's OAuth clients: every verb the main lane has, on its
1148
+ * `admin` variant — except `setAutonomousAgents`, which has none. Binding an agent grants a credential standing
1149
+ * authority to act unattended INSIDE a tenant, so only that tenant's own administrator may do it (#1611); the
1150
+ * operator can still see what a client runs, and end the client.
1151
+ *
1152
+ * It ALSO carries the main lane's views. An application that serves tenants and operators from one frontend
1153
+ * composes both lanes, and this entry REPLACES the main one there — so without the default-variant views the
1154
+ * tenant administrator's own Settings pane (which runs the DEFAULT variants) would have no Create and no binding
1155
+ * action at all. In a split operator-only deployment they are role-gated like everywhere else (the tenant
1156
+ * lane's operations admit tenant roles only), but note the main LIST view is ADDRESSABLE: such a frontend
1157
+ * registers an organization-scoped list route that an operator who is also a member of an organization can
1158
+ * reach. No operator-only deployment exists yet; if one is built, mark the spread LIST not addressable here.
1159
+ */
1160
+ [R.OAUTH_CLIENTS_ORGANIZATION]: resourceUIBehaviorFrom(orgOAuthClientsFactory, main[R.OAUTH_CLIENTS_ORGANIZATION], ({ base, opsVariantResolver }) => ({
1161
+ icon: base.icon,
1162
+ layout: base.layout,
1163
+ getFrontendResourceLabel: base.getFrontendResourceLabel,
1164
+ fields: base.fields,
1165
+ views: [
1166
+ ...(base.views ?? []),
1167
+ [opsVariantResolver(Op.READ, 'admin'), notAddressable()],
1168
+ [opsVariantResolver(Op.LIST, 'admin'), notAddressable({ collectionDisplayConfig: REGISTRY_TABLE })],
1169
+ [opsVariantResolver(Op.SEARCH, 'admin'), notAddressable()],
1170
+ [opsVariantResolver(Op.CREATE, 'admin'), notAddressable()],
1171
+ [opsVariantResolver(Op.DELETE, 'admin'), notAddressable()],
1172
+ [opsVariantResolver(OrgOAuthClients_Operations.ROTATE, 'admin'), notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1173
+ [opsVariantResolver(OrgOAuthClients_Operations.REGENERATE, 'admin'), notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1174
+ [opsVariantResolver(OrgOAuthClients_Operations.DEACTIVATE, 'admin'), notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1175
+ [opsVariantResolver(OrgOAuthClients_Operations.REACTIVATE, 'admin'), notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1176
+ ],
1177
+ })),
757
1178
  [R.ORG_API_KEYS]: resourceUIBehaviorFrom(orgApiKeysFactory, main[R.ORG_API_KEYS], ({ base, opsVariantResolver }) => ({
758
1179
  icon: base.icon,
759
1180
  layout: base.layout,
760
1181
  getFrontendResourceLabel: base.getFrontendResourceLabel,
761
1182
  fields: base.fields,
762
1183
  views: [
1184
+ // The tenant administrator's own views, kept. In an application composing both lanes this entry replaces the
1185
+ // main one, and the Settings pane runs the DEFAULT variants: with only the `admin` keys below, the organization's
1186
+ // API keys rendered with no Create and no declared list shape (#1812 measured it beside the OAuth clients).
1187
+ ...(base.views ?? []),
763
1188
  [opsVariantResolver(Op.READ, 'admin'), notAddressable()],
764
- [opsVariantResolver(Op.LIST, 'admin'), notAddressable()],
1189
+ // The registry TABLE and the action icons the main lane declares. This lane REPLACES the main entry in an
1190
+ // application that composes both scopes, so without them the list rendered as stacked cards and every
1191
+ // row action showed the missing-icon placeholder.
1192
+ [opsVariantResolver(Op.LIST, 'admin'), notAddressable({ collectionDisplayConfig: REGISTRY_TABLE })],
765
1193
  [opsVariantResolver(Op.SEARCH, 'admin'), notAddressable()],
766
1194
  [opsVariantResolver(Op.CREATE, 'admin'), notAddressable()],
767
1195
  [opsVariantResolver(Op.DELETE, 'admin'), notAddressable()],
768
- [opsVariantResolver(OrgApiKeys_Operations.ROTATE, 'admin'), notAddressable()],
769
- [opsVariantResolver(OrgApiKeys_Operations.REGENERATE, 'admin'), notAddressable()],
770
- [opsVariantResolver(OrgApiKeys_Operations.DEACTIVATE, 'admin'), notAddressable()],
771
- [opsVariantResolver(OrgApiKeys_Operations.REACTIVATE, 'admin'), notAddressable()],
772
- [opsVariantResolver(OrgApiKeys_Operations.EXTEND_EXPIRY, 'admin'), notAddressable()],
773
- [opsVariantResolver(OrgApiKeys_Operations.USAGE_STATS, 'admin'), notAddressable()],
1196
+ [opsVariantResolver(OrgApiKeys_Operations.ROTATE, 'admin'), notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1197
+ [opsVariantResolver(OrgApiKeys_Operations.REGENERATE, 'admin'), notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1198
+ [opsVariantResolver(OrgApiKeys_Operations.DEACTIVATE, 'admin'), notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1199
+ [opsVariantResolver(OrgApiKeys_Operations.REACTIVATE, 'admin'), notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1200
+ [opsVariantResolver(OrgApiKeys_Operations.EXTEND_EXPIRY, 'admin'), notAddressable({ iconOverride: CalendarPlus, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
1201
+ [opsVariantResolver(OrgApiKeys_Operations.USAGE_STATS, 'admin'), notAddressable({ iconOverride: ChartColumn })],
774
1202
  ],
775
1203
  })),
776
1204
  };
@@ -779,6 +1207,8 @@ export const securityMainPresets = buildSecurityMainPresets();
779
1207
  export const securityAdminPresets = buildSecurityAdminPresets(securityMainPresets);
780
1208
  export const orgApiKeysMainResourceUIBehavior = securityMainPresets[R.ORG_API_KEYS];
781
1209
  export const orgApiKeysAdminResourceUIBehavior = securityAdminPresets[R.ORG_API_KEYS];
1210
+ export const orgOAuthClientsMainResourceUIBehavior = securityMainPresets[R.OAUTH_CLIENTS_ORGANIZATION];
1211
+ export const orgOAuthClientsAdminResourceUIBehavior = securityAdminPresets[R.OAUTH_CLIENTS_ORGANIZATION];
782
1212
  export const auditLogsMainResourceUIBehavior = securityMainPresets[R.AUDIT_LOGS];
783
1213
  export const applicationAuditLogsAdminResourceUIBehavior = securityAdminPresets[R.APPLICATION_AUDIT_LOGS];
784
1214
  //# sourceMappingURL=security.resource-presets.js.map