@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,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { RelatedResourceSections, resourceUIBehavior, resourceUIBehaviorFrom, SummaryFlow, SummaryMeta, SummaryTitle, ResourceLayoutInlineEditDefault, } from '@wildo-ai/saas-frontend-lib';
2
+ import { RelatedResourceSections, ResourceHeaderTitle, resourceUIBehavior, resourceUIBehaviorFrom, SummaryFlow, SummaryMeta, SummaryTitle, ResourceLayoutInlineEditDefault, } from '@wildo-ai/saas-frontend-lib';
3
3
  import { DisplayField, DisplayLayout, FormField, FormLayout, ObjectScope, Section, SummaryLayout, SummaryTitlePlacement, } from '@wildo-ai/saas-frontend-lib';
4
- import { BadgeSemanticVariant, DisplayChromeMode, EditChromeMode, EnumDisplayMode, SectionMode } from '@wildo-ai/zod-decorators';
5
- import { CalendarPlus, ChartColumn, GitBranch, Key, KeyRound, ShieldCheck, ShieldX, Ban, Hourglass, LayoutGrid, Power, PowerOff, RefreshCw, RotateCcw, RotateCw, ToggleLeft, ToggleRight, } from 'lucide-react';
4
+ import { BadgeSemanticVariant, DisplayChromeMode, EditChromeMode, EnumDisplayMode, ForeignKeyDisplayMode, SectionMode, StringEditMode, } from '@wildo-ai/zod-decorators';
5
+ import { BadgeCheck, CalendarPlus, FileText, ChartColumn, GitBranch, Key, KeyRound, ShieldCheck, ShieldX, Ban, Hourglass, LayoutGrid, Power, PowerOff, RefreshCw, RotateCcw, RotateCw, ToggleLeft, ToggleRight, } from 'lucide-react';
6
6
  import { CoreResourceType as R, CoreResourceOperation as Op, ApiKeyStatus, AppApiKeys_Operations, ApplicationFeatures_Operations, applicationResourceConfiguration_InitializationFactory as appFactory, applicationVersionsResourceConfiguration_InitializationFactory as appVersionsFactory, applicationMetadataResourceConfiguration_InitializationFactory as appMetadataFactory, appApiKeysResourceConfiguration_InitializationFactory as appApiKeysFactory, appOAuthClientsResourceConfiguration_InitializationFactory as appOAuthClientsFactory, applicationAccessGrantsResourceConfiguration_InitializationFactory as appAccessGrantsFactory, ApplicationAccessGrants_Operations, ApplicationAccessGrantSchema, PlatformAccessGrantStatus, AppOAuthClients_Operations, OAuthClientApplicationSchema, OAuthClientStatus, applicationFeaturesResourceConfiguration_InitializationFactory as appFeaturesFactory, ApplicationSchema, ApplicationVersionSchema, ApplicationMetadataSchema, ApiKeyApplicationSchema, ScopeFeaturesWithOverridesSchema, } from '@wildo-ai/saas-models';
7
- import { addressable, notAddressable, apiOnly } from './resource.config.utils.js';
7
+ import { ApiKeyRotationWindowRows } from './api-key-rotation-window.js';
8
+ import { addressable, notAddressable, apiOnly, ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE } from './resource.config.utils.js';
8
9
  /**
9
10
  * Application — Resource frontend presets
10
11
  *
@@ -67,19 +68,28 @@ export function buildApplicationAdminPresets(main) {
67
68
  })),
68
69
  [R.APPLICATION_VERSIONS]: resourceUIBehavior(appVersionsFactory, ApplicationVersionSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
69
70
  icon: GitBranch,
71
+ // A release is named by its version string, so the page title IS the version and no row repeats it.
70
72
  getFrontendResourceLabel: ({ currentObject }) => currentObject.version,
71
73
  layout: {
72
74
  refs: {
73
75
  sections: ['overview', 'releaseNotes'],
74
76
  },
75
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
76
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
77
- display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "version" }), _jsx(DisplayField, { name: "releaseDate" })] }), _jsx(Section, { name: "releaseNotes", children: _jsx(DisplayField, { name: "releaseNotes" }) })] })),
77
+ // Settings rows on the read page, bare values on a card, and a STACKED form: a release is authored
78
+ // content (a version string and a paragraph of notes), not a list of settings.
79
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
80
+ display: () => (_jsxs(DisplayLayout, { children: [_jsx(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: _jsx(DisplayField, { name: "releaseDate" }) }), _jsx(Section, { name: "releaseNotes", appearance: SectionMode.CARD, chromeMode: DisplayChromeMode.HIDDEN, hideDescription: true, children: _jsx(DisplayField, { name: "releaseNotes" }) })] })),
78
81
  summary: () => (
79
- // TEMPLATE_OWNED with no title: this card's LABEL is the version string, which the first
80
- // field already renders. At AUTO the row read "1.4.2 · Version: 1.4.2 · Released: …".
81
- _jsxs(SummaryLayout, { flow: SummaryFlow.ROW, titlePlacement: SummaryTitlePlacement.TEMPLATE_OWNED, children: [_jsx(DisplayField, { name: "version" }), _jsx(DisplayField, { name: "releaseDate" })] })),
82
- edit: () => (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(FormField, { name: "version" }), _jsx(FormField, { name: "releaseDate" })] }), _jsx(Section, { name: "releaseNotes", children: _jsx(FormField, { name: "releaseNotes" }) })] })),
82
+ // One line: the version and the day it shipped, bare. TEMPLATE_OWNED with no title, because the card's
83
+ // LABEL is the version string, which the first field already renders. At AUTO the row read
84
+ // "1.4.2 · Version: 1.4.2 · Released: …".
85
+ _jsxs(SummaryLayout, { flow: SummaryFlow.ROW, titlePlacement: SummaryTitlePlacement.TEMPLATE_OWNED, children: [_jsx(DisplayField, { name: "version", className: "w-auto" }), _jsx(DisplayField, { name: "releaseDate", className: "w-auto" })] })),
86
+ // One flat form: three fields need no headings, and "Release Notes" over the only field in its section
87
+ // said what the field's own label says.
88
+ edit: () => (_jsxs(FormLayout, { children: [_jsx(FormField, { name: "version", autoFocus: true }), _jsx(FormField, { name: "releaseDate" }), _jsx(FormField, { name: "releaseNotes" })] })),
89
+ },
90
+ fields: {
91
+ // Release notes are a paragraph, not a line.
92
+ releaseNotes: schemaShape.releaseNotes.stringUI({ edit: { editMode: StringEditMode.TEXTAREA } }),
83
93
  },
84
94
  views: [
85
95
  // Versions are created by the app-version system (API-driven) — headless (API-only).
@@ -92,12 +102,14 @@ export function buildApplicationAdminPresets(main) {
92
102
  ],
93
103
  })),
94
104
  [R.APPLICATION_METADATA]: resourceUIBehavior(appMetadataFactory, ApplicationMetadataSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
105
+ // The header showed the registry's missing-icon placeholder: nothing had ever named one.
106
+ icon: FileText,
95
107
  layout: {
96
- refs: {
97
- sections: ['overview'],
98
- },
99
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
100
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
108
+ // No sections. The record is one field — the application's description — and an "Overview" card with a
109
+ // "Purpose, audience, and positioning" description over it was a heading and a prompt per field.
110
+ // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows, bare on a card,
111
+ // and a STACKED form, because the description is a paragraph someone writes.
112
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
101
113
  // Application metadata — the application’s own description. A settings pane is a list of decisions an operator came to TAKE, so its
102
114
  // writable fields are offered in place rather than behind a whole-pane edit mode. The set is
103
115
  // DERIVED (not primary key, not excludeFromUpdate, not hidden, not opted out), so a field
@@ -105,13 +117,16 @@ export function buildApplicationAdminPresets(main) {
105
117
  defaultInlineEdit: ResourceLayoutInlineEditDefault.WRITABLE_FIELDS,
106
118
  // And its empty rows say WHICH emptiness they are — "None yet" for a list with no members,
107
119
  // "Not available" for a computed value, "No value" for a field awaiting input — instead of one
108
- // em dash standing for all three. The surface policy's dash is right where a reader is scanning;
109
- // on a settings row, which is read one at a time to decide something, it is an answer that
110
- // refuses to answer.
120
+ // em dash standing for all three. Here the empty description IS the information: the seed writes
121
+ // an empty one, and the pane is where somebody fills it in.
111
122
  defaultEmptyValue: 'placeholder',
112
- display: () => (_jsx(DisplayLayout, { children: _jsx(Section, { name: "overview", appearance: SectionMode.CARD, children: _jsx(DisplayField, { name: "description" }) }) })),
123
+ display: () => (_jsx(DisplayLayout, { children: _jsx(DisplayField, { name: "description" }) })),
113
124
  summary: () => (_jsx(SummaryLayout, { children: _jsx(DisplayField, { name: "description" }) })),
114
- edit: () => (_jsx(FormLayout, { children: _jsx(Section, { name: "overview", appearance: SectionMode.CARD, children: _jsx(FormField, { name: "description" }) }) })),
125
+ edit: () => (_jsx(FormLayout, { children: _jsx(FormField, { name: "description", autoFocus: true }) })),
126
+ },
127
+ fields: {
128
+ // A description is a paragraph, not a line.
129
+ description: schemaShape.description.stringUI({ edit: { editMode: StringEditMode.TEXTAREA } }),
115
130
  },
116
131
  views: [
117
132
  // Metadata is system-managed, auto-initialized with the app — headless (API-only).
@@ -122,13 +137,21 @@ export function buildApplicationAdminPresets(main) {
122
137
  ],
123
138
  })),
124
139
  [R.APPLICATION_FEATURES]: resourceUIBehavior(appFeaturesFactory, ScopeFeaturesWithOverridesSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
140
+ // The header showed the registry's missing-icon placeholder: nothing had ever named one. The same glyph as
141
+ // the organization's entitlements, because it is the same record at another scope.
142
+ icon: BadgeCheck,
125
143
  layout: {
126
144
  refs: { sections: ['overview', 'overrides'] },
127
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
128
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
129
- display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "features" }), _jsx(DisplayField, { name: "limits" })] }), _jsx(Section, { name: "overrides", children: _jsxs(ObjectScope, { name: "manualOverrides", children: [_jsx(DisplayField, { name: "addedFeatures" }), _jsx(DisplayField, { name: "removedFeatures" }), _jsx(DisplayField, { name: "limitOverrides" })] }) })] })),
145
+ // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows on read, bare
146
+ // values on a card. The form STACKS, because every field it edits is a list or a map of entries, which
147
+ // needs the full width a settings row gives to its label.
148
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.STACKED },
149
+ // An override list with nothing in it IS the answer on this pane — "no feature was granted or withdrawn
150
+ // by hand" — so it says "None yet" rather than an em dash that reads as missing data.
151
+ defaultEmptyValue: 'placeholder',
152
+ display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "features" }), _jsx(DisplayField, { name: "limits" })] }), _jsx(Section, { name: "overrides", hideDescription: true, children: _jsxs(ObjectScope, { name: "manualOverrides", children: [_jsx(DisplayField, { name: "addedFeatures" }), _jsx(DisplayField, { name: "removedFeatures" }), _jsx(DisplayField, { name: "limitOverrides" })] }) })] })),
130
153
  summary: () => (_jsxs(SummaryLayout, { children: [_jsx(DisplayField, { name: "features" }), _jsx(DisplayField, { name: "limits" })] })),
131
- edit: () => (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(FormField, { name: "features" }), _jsx(FormField, { name: "limits" })] }), _jsx(Section, { name: "overrides", children: _jsxs(ObjectScope, { name: "manualOverrides", children: [_jsx(FormField, { name: "addedFeatures" }), _jsx(FormField, { name: "removedFeatures" }), _jsx(FormField, { name: "limitOverrides" })] }) })] })),
154
+ edit: () => (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(FormField, { name: "features" }), _jsx(FormField, { name: "limits" })] }), _jsx(Section, { name: "overrides", children: _jsxs(ObjectScope, { name: "manualOverrides", children: [_jsx(FormField, { name: "addedFeatures" }), _jsx(FormField, { name: "removedFeatures" }), _jsx(FormField, { name: "limitOverrides" })] }) })] })),
132
155
  },
133
156
  views: [
134
157
  [Op.READ, notAddressable()],
@@ -143,17 +166,24 @@ export function buildApplicationAdminPresets(main) {
143
166
  icon: Key,
144
167
  layout: {
145
168
  refs: {
146
- sections: ['overview', 'access', 'lifecycle'],
169
+ sections: ['overview', 'lifecycle'],
147
170
  },
148
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
149
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
150
- display: () => (_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" })] })] })),
151
- summary: () => (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "scope" }), _jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "keyPrefix" })] })] })),
152
- edit: () => (_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" })] })] })),
171
+ // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows on read and on the
172
+ // form — a credential is configured, not written — and bare values on a card.
173
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
174
+ // The identity line: the key's name (the page title) and its status beside it. Neither is repeated below.
175
+ 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" })] })),
176
+ display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(DisplayField, { name: "keyPrefix" }), _jsx(DisplayField, { name: "description" }), _jsx(DisplayField, { name: "roles" })] }), _jsxs(Section, { name: "lifecycle", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "createdAt" }), _jsx(ApiKeyRotationWindowRows, {})] })] })),
177
+ summary: () => (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "keyPrefix" }), _jsx(DisplayField, { name: "expiresAt" })] })] })),
178
+ // One flat form. The name first, because it is what the key is called everywhere; the description as a
179
+ // paragraph. Headings over two or three fields of one short form separated nothing.
180
+ edit: () => (_jsxs(FormLayout, { children: [_jsx(FormField, { name: "name", autoFocus: true }), _jsx(FormField, { name: "description" }), _jsx(FormField, { name: "status" }), _jsx(FormField, { name: "expiresAt" }), _jsx(FormField, { name: "roles" })] })),
153
181
  },
154
182
  fields: {
183
+ // The badge's colour carries the state; the state's description is a form hint, not a line under every
184
+ // badge on a list.
155
185
  status: schemaShape.status.enumUI({
156
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
186
+ display: { displayMode: EnumDisplayMode.BADGE },
157
187
  edit: { showDescription: true },
158
188
  values: {
159
189
  [ApiKeyStatus.ACTIVE]: { color: BadgeSemanticVariant.SUCCESS },
@@ -161,6 +191,23 @@ export function buildApplicationAdminPresets(main) {
161
191
  [ApiKeyStatus.EXPIRED]: { color: BadgeSemanticVariant.WARNING },
162
192
  },
163
193
  }),
194
+ description: schemaShape.description.stringUI({
195
+ display: { hideWhenEmpty: true },
196
+ edit: { editMode: StringEditMode.TEXTAREA },
197
+ }),
198
+ // A key with no expiry never expires, and one never used has no last use: an empty row says nothing.
199
+ // On a card the expiry keeps its label — a bare date beside a name and a status could be any of the key's
200
+ // dates.
201
+ expiresAt: schemaShape.expiresAt.dateUI({
202
+ display: { hideWhenEmpty: true },
203
+ summaryOverride: { chromeMode: DisplayChromeMode.INLINE },
204
+ }),
205
+ lastUsedAt: schemaShape.lastUsedAt.dateUI({ display: { hideWhenEmpty: true } }),
206
+ // The rotation window, rendered only while the previous secret still authenticates (`ApiKeyRotationWindowRows`).
207
+ // The prefix row says what an empty end date means — after a regenerate the old secret has none and keeps
208
+ // working — so it carries its description; the end-date row hides itself when there is no date.
209
+ previousKeyPrefix: schemaShape.previousKeyPrefix.stringUI({ display: { hideWhenEmpty: true, showDescription: true } }),
210
+ previousKeyInvalidationDate: schemaShape.previousKeyInvalidationDate.dateUI({ display: { hideWhenEmpty: true } }),
164
211
  },
165
212
  getFrontendResourceLabel: ({ currentObject }) => currentObject.name,
166
213
  views: [
@@ -168,11 +215,11 @@ export function buildApplicationAdminPresets(main) {
168
215
  [Op.LIST, notAddressable()],
169
216
  [Op.SEARCH, notAddressable()],
170
217
  [Op.CREATE, notAddressable()],
171
- [AppApiKeys_Operations.ROTATE, notAddressable({ iconOverride: RotateCw })],
172
- [AppApiKeys_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw })],
173
- [AppApiKeys_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff })],
174
- [AppApiKeys_Operations.REACTIVATE, notAddressable({ iconOverride: Power })],
175
- [AppApiKeys_Operations.EXTEND_EXPIRY, notAddressable({ iconOverride: CalendarPlus })],
218
+ [AppApiKeys_Operations.ROTATE, notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
219
+ [AppApiKeys_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
220
+ [AppApiKeys_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
221
+ [AppApiKeys_Operations.REACTIVATE, notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
222
+ [AppApiKeys_Operations.EXTEND_EXPIRY, notAddressable({ iconOverride: CalendarPlus, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
176
223
  [AppApiKeys_Operations.USAGE_STATS, notAddressable({ iconOverride: ChartColumn })],
177
224
  ],
178
225
  })),
@@ -201,18 +248,22 @@ export function buildApplicationAdminPresets(main) {
201
248
  refs: {
202
249
  sections: ['overview', 'access', 'redirects', 'lifecycle'],
203
250
  },
204
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
205
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
206
- display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "name" }), _jsx(DisplayField, { name: "description" }), _jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "secretPrefix" })] }), _jsxs(Section, { name: "access", 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", children: [_jsx(DisplayField, { name: "redirectUris" }), _jsx(DisplayField, { name: "postLogoutRedirectUris" })] }), _jsxs(Section, { name: "lifecycle", children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "createdAt" })] })] })),
251
+ // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows on read and on the
252
+ // form — a client is configured, not written — and bare values on a card.
253
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
254
+ // The identity line: the client's name (the page title) and its status beside it. Neither is repeated below.
255
+ 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" })] })),
256
+ display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_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" })] }), _jsxs(Section, { name: "lifecycle", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "lastUsedAt" }), _jsx(DisplayField, { name: "createdAt" })] })] })),
207
257
  summary: () => (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "name" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "secretPrefix" }), _jsx(DisplayField, { name: "expiresAt" })] })] })),
208
- edit: () => (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, children: [_jsx(FormField, { name: "name" }), _jsx(FormField, { name: "description" }), _jsx(FormField, { name: "expiresAt" })] }), _jsxs(Section, { name: "access", children: [_jsx(FormField, { name: "allowedGrants" }), _jsx(FormField, { name: "allowedScopes" }), _jsx(FormField, { name: "roles" }), _jsx(FormField, { name: "tokenEndpointAuthMethod" }), _jsx(FormField, { name: "requiresConsent" })] }), _jsxs(Section, { name: "redirects", children: [_jsx(FormField, { name: "redirectUris" }), _jsx(FormField, { name: "postLogoutRedirectUris" })] })] })),
258
+ edit: () => (_jsxs(FormLayout, { children: [_jsxs(Section, { name: "overview", appearance: SectionMode.CARD, hideTitle: true, hideDescription: true, children: [_jsx(FormField, { name: "name", autoFocus: true }), _jsx(FormField, { name: "description" }), _jsx(FormField, { name: "expiresAt" })] }), _jsxs(Section, { name: "access", hideDescription: true, children: [_jsx(FormField, { name: "allowedGrants" }), _jsx(FormField, { name: "allowedScopes" }), _jsx(FormField, { name: "roles" }), _jsx(FormField, { name: "tokenEndpointAuthMethod" }), _jsx(FormField, { name: "requiresConsent" })] }), _jsxs(Section, { name: "redirects", children: [_jsx(FormField, { name: "redirectUris" }), _jsx(FormField, { name: "postLogoutRedirectUris" })] })] })),
209
259
  },
210
260
  fields: {
211
261
  // Same three states and the same colours as an API key, because they mean the same things.
212
262
  // EXPIRED is a WARNING rather than a DESTRUCTIVE: it is the end of a validity window, not a
213
- // failure, and it is the one state `reactivate` deliberately does NOT revive.
263
+ // failure, and it is the one state `reactivate` deliberately does NOT revive. The state's description
264
+ // is a form hint; under every badge on a list it read "Whether the client can obtain tokens…" per row.
214
265
  status: schemaShape.status.enumUI({
215
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
266
+ display: { displayMode: EnumDisplayMode.BADGE },
216
267
  edit: { showDescription: true },
217
268
  values: {
218
269
  [OAuthClientStatus.ACTIVE]: { color: BadgeSemanticVariant.SUCCESS },
@@ -220,6 +271,20 @@ export function buildApplicationAdminPresets(main) {
220
271
  [OAuthClientStatus.EXPIRED]: { color: BadgeSemanticVariant.WARNING },
221
272
  },
222
273
  }),
274
+ description: schemaShape.description.stringUI({
275
+ display: { hideWhenEmpty: true },
276
+ edit: { editMode: StringEditMode.TEXTAREA },
277
+ }),
278
+ // A client-credentials client has no sign-out redirect, a client with no expiry never expires, and one
279
+ // never used has no last use: each empty row said nothing.
280
+ postLogoutRedirectUris: schemaShape.postLogoutRedirectUris.arrayUI({ display: { hideWhenEmpty: true } }),
281
+ // On a card the expiry keeps its label: a bare date beside a name and a status could be any of the
282
+ // client's dates.
283
+ expiresAt: schemaShape.expiresAt.dateUI({
284
+ display: { hideWhenEmpty: true },
285
+ summaryOverride: { chromeMode: DisplayChromeMode.INLINE },
286
+ }),
287
+ lastUsedAt: schemaShape.lastUsedAt.dateUI({ display: { hideWhenEmpty: true } }),
223
288
  },
224
289
  getFrontendResourceLabel: ({ currentObject }) => currentObject.name,
225
290
  views: [
@@ -227,10 +292,10 @@ export function buildApplicationAdminPresets(main) {
227
292
  [Op.LIST, notAddressable()],
228
293
  [Op.SEARCH, notAddressable()],
229
294
  [Op.CREATE, notAddressable()],
230
- [AppOAuthClients_Operations.ROTATE, notAddressable({ iconOverride: RotateCw })],
231
- [AppOAuthClients_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw })],
232
- [AppOAuthClients_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff })],
233
- [AppOAuthClients_Operations.REACTIVATE, notAddressable({ iconOverride: Power })],
295
+ [AppOAuthClients_Operations.ROTATE, notAddressable({ iconOverride: RotateCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
296
+ [AppOAuthClients_Operations.REGENERATE, notAddressable({ iconOverride: RefreshCw, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
297
+ [AppOAuthClients_Operations.DEACTIVATE, notAddressable({ iconOverride: PowerOff, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
298
+ [AppOAuthClients_Operations.REACTIVATE, notAddressable({ iconOverride: Power, ...ROW_VERB_ON_RESOURCE_WITHOUT_UPDATE })],
234
299
  // Dispatched by the expiry sweep rather than by a person — headless, so it must carry no
235
300
  // view fields at all. An operator ends a client early with DEACTIVATE.
236
301
  [AppOAuthClients_Operations.EXPIRE, apiOnly()],
@@ -257,30 +322,40 @@ export function buildApplicationAdminPresets(main) {
257
322
  */
258
323
  [R.APPLICATION_ACCESS_GRANTS]: resourceUIBehavior(appAccessGrantsFactory, ApplicationAccessGrantSchema, ({ schema, schemaShape, opsVariantResolver }) => ({
259
324
  icon: ShieldCheck,
260
- // The REQUESTING OPERATOR. Its own specification already says why — "the first question
261
- // asked of any elevation is WHO holds it" — and documented `frontend.label` on that basis
262
- // while this preset declared no label strategy at all. That combination THROWS at module
263
- // scope in `resourceSpecificationFromFrontendConfiguration`, and because the engine's spec
264
- // barrel evaluates every spec on import, one unmatched pair took the whole
265
- // `@wildo-ai/saas-specifications` package down for every consumer, application companions
266
- // included. Declaring it here is what makes the documentation true.
267
- getFrontendResourceLabel: ({ currentObject }) => currentObject.operatorUserId,
325
+ // A request is named by the instant it was made, in the viewer's date-time format. It was named by the
326
+ // requesting operator's user id, which put a raw `6aa25215267f7e7758312323` at the top of the page and as the
327
+ // title of every card: this callback is synchronous over the record, and the record carries the operator's id
328
+ // alone, never their name. WHO asked is still the first thing the body and the card show, resolved to the
329
+ // person by the foreign-key renderer (see `operatorUserId` below). `createdAt` is optional on the row, so a
330
+ // row without it falls back to its id rather than to nothing.
331
+ //
332
+ // (History: this preset once declared no label strategy at all while the specification documented
333
+ // `frontend.label`, and that pair THROWS at module scope in `resourceSpecificationFromFrontendConfiguration`,
334
+ // taking the whole `@wildo-ai/saas-specifications` package down. Keep the two declared together.)
335
+ getFrontendResourceLabel: ({ currentObject, formatDateTime }) => currentObject.createdAt ? formatDateTime(currentObject.createdAt) : currentObject._id,
268
336
  layout: {
269
337
  refs: {
270
338
  sections: ['request', 'decision', 'window'],
271
339
  },
272
- // Settings-page chrome (Settings Hub → Administration): label-left / value-right rows.
273
- defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.INLINE, edit: EditChromeMode.SETTINGS_ROW },
274
- display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "request", appearance: SectionMode.CARD, children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "operatorUserId" }), _jsx(DisplayField, { name: "justification" }), _jsx(DisplayField, { name: "createdAt" })] }), _jsxs(Section, { name: "decision", children: [_jsx(DisplayField, { name: "approvedByUserId" }), _jsx(DisplayField, { name: "approvedAt" }), _jsx(DisplayField, { name: "decisionReason" })] }), _jsxs(Section, { name: "window", children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "grantedMachineCredential" })] })] })),
275
- summary: () => (_jsxs(SummaryLayout, { children: [_jsx(SummaryTitle, { name: "operatorUserId" }), _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "expiresAt" })] })] })),
340
+ // Settings rows on the read page, bare values on a card. There is no form: every state change is a named verb.
341
+ defaultChromeMode: { display: DisplayChromeMode.SETTINGS_ROW, summary: DisplayChromeMode.HIDDEN, edit: EditChromeMode.SETTINGS_ROW },
342
+ // The identity line: when it was asked (the title) and where it stands. Neither is repeated below.
343
+ 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" })] })),
344
+ // The three headings stay — they are what keeps the requester and the approver in separate blocks, which is
345
+ // the distinction this control exists for. Their descriptions do not: each explained the control to a
346
+ // reader who is looking at one of its records.
347
+ display: () => (_jsxs(DisplayLayout, { children: [_jsxs(Section, { name: "request", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "operatorUserId" }), _jsx(DisplayField, { name: "justification" })] }), _jsxs(Section, { name: "decision", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "approvedByUserId" }), _jsx(DisplayField, { name: "approvedAt" }), _jsx(DisplayField, { name: "decisionReason" })] }), _jsxs(Section, { name: "window", appearance: SectionMode.CARD, hideDescription: true, children: [_jsx(DisplayField, { name: "expiresAt" }), _jsx(DisplayField, { name: "grantedMachineCredential" })] })] })),
348
+ // The instant is the card's title (AUTO); below it, who asked, where it stands, and when the window ends.
349
+ summary: () => (_jsx(SummaryLayout, { children: _jsxs(SummaryMeta, { children: [_jsx(DisplayField, { name: "operatorUserId" }), _jsx(DisplayField, { name: "status" }), _jsx(DisplayField, { name: "expiresAt" })] }) })),
276
350
  },
277
351
  fields: {
278
352
  // PENDING_APPROVAL is a WARNING rather than a neutral secondary: an unsigned request is the
279
353
  // state a reviewer must act on, and the one state where the window does not yet exist.
280
354
  // DENIED and REVOKED are both DESTRUCTIVE because both are a refusal; EXPIRED is not — it is
281
- // the control working, a window closing on its own.
355
+ // the control working, a window closing on its own. The state's description ("Active status requires a
356
+ // valid, unexpired access window.") read under every badge on the queue, so it is not shown.
282
357
  status: schemaShape.status.enumUI({
283
- display: { displayMode: EnumDisplayMode.BADGE, showDescription: true },
358
+ display: { displayMode: EnumDisplayMode.BADGE },
284
359
  values: {
285
360
  [PlatformAccessGrantStatus.PENDING_APPROVAL]: { color: BadgeSemanticVariant.WARNING },
286
361
  [PlatformAccessGrantStatus.ACTIVE]: { color: BadgeSemanticVariant.SUCCESS },
@@ -289,6 +364,17 @@ export function buildApplicationAdminPresets(main) {
289
364
  [PlatformAccessGrantStatus.EXPIRED]: { color: BadgeSemanticVariant.SECONDARY },
290
365
  },
291
366
  }),
367
+ // Both identities resolve to the PERSON, never an id: a reviewer is asking "who", and a hex id answers
368
+ // nobody.
369
+ operatorUserId: schemaShape.operatorUserId.foreignKeyUI({ display: { mode: ForeignKeyDisplayMode.LABEL } }),
370
+ // Empty until somebody signs, and on a PENDING request that emptiness is the answer ("not signed yet"), so
371
+ // the approver row stays; the reason is optional even on a decision, and hides.
372
+ approvedByUserId: schemaShape.approvedByUserId.foreignKeyUI({ display: { mode: ForeignKeyDisplayMode.LABEL } }),
373
+ decisionReason: schemaShape.decisionReason.stringUI({ display: { hideWhenEmpty: true } }),
374
+ // Present only when the grant was requested for a machine credential.
375
+ grantedMachineCredential: schemaShape.grantedMachineCredential.objectUI({ display: { hideWhenEmpty: true } }),
376
+ // On a card the expiry keeps its label: a bare date beside a name and a status could be the request's.
377
+ expiresAt: schemaShape.expiresAt.dateUI({ summaryOverride: { chromeMode: DisplayChromeMode.INLINE } }),
292
378
  },
293
379
  views: [
294
380
  [Op.READ, notAddressable()],