@wildo-ai/saas-frontend-lib 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/companion-exports.d.ts +1 -1
- package/dist/esm/companion-exports.d.ts.map +1 -1
- package/dist/esm/companion-exports.js +1 -1
- package/dist/esm/companion-exports.js.map +1 -1
- package/dist/esm/components/ai-elements/decision/approval.d.ts +13 -1
- package/dist/esm/components/ai-elements/decision/approval.d.ts.map +1 -1
- package/dist/esm/components/ai-elements/decision/approval.js.map +1 -1
- package/dist/esm/components/flows-actors/approval.d.ts.map +1 -1
- package/dist/esm/components/flows-actors/approval.js +12 -7
- package/dist/esm/components/flows-actors/approval.js.map +1 -1
- package/dist/esm/components/flows-actors/approval.labels.d.ts.map +1 -1
- package/dist/esm/components/flows-actors/approval.labels.js +3 -0
- package/dist/esm/components/flows-actors/approval.labels.js.map +1 -1
- package/dist/esm/components/flows-actors/execution-plan.js +1 -1
- package/dist/esm/components/flows-actors/execution-plan.js.map +1 -1
- package/dist/esm/components/flows-actors/task-memory.js +1 -1
- package/dist/esm/components/flows-actors/task-memory.js.map +1 -1
- package/dist/esm/components/form/automated/generated-relationship-field.hooks.d.ts +1 -1
- package/dist/esm/components/form/controls/number/slider.d.ts +1 -1
- package/dist/esm/components/form/fields/form-field-file-provider-constraints.frontend.utils.d.ts +1 -1
- package/dist/esm/components/form/fields/form-field-file-provider-constraints.frontend.utils.d.ts.map +1 -1
- package/dist/esm/components/form/fields/form-field-file-provider-constraints.frontend.utils.js.map +1 -1
- package/dist/esm/components/form/fields/form-field-file.d.ts.map +1 -1
- package/dist/esm/components/form/fields/form-field-file.js +66 -17
- package/dist/esm/components/form/fields/form-field-file.js.map +1 -1
- package/dist/esm/components/form/fields/form-field-file.labels.js +2 -2
- package/dist/esm/components/form/fields/form-field-file.labels.js.map +1 -1
- package/dist/esm/components/form/form-field-types.d.ts +20 -0
- package/dist/esm/components/form/form-field-types.d.ts.map +1 -1
- package/dist/esm/components/form/form-field-types.js.map +1 -1
- package/dist/esm/components/navigation/NavigationItemBadge.d.ts +7 -1
- package/dist/esm/components/navigation/NavigationItemBadge.d.ts.map +1 -1
- package/dist/esm/components/navigation/NavigationItemBadge.js +29 -1
- package/dist/esm/components/navigation/NavigationItemBadge.js.map +1 -1
- package/dist/esm/components/navigation/menu-bar.d.ts +1 -1
- package/dist/esm/components/overlays/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/esm/components/overlays/containers/OverlayContainer.js +36 -9
- package/dist/esm/components/overlays/containers/OverlayContainer.js.map +1 -1
- package/dist/esm/components/overlays/floating/context-menu.d.ts +1 -1
- package/dist/esm/components/overlays/floating/dropdown-menu.d.ts +1 -1
- package/dist/esm/components/resources/ResourceLayout_FilterChips.js +1 -1
- package/dist/esm/components/resources/ResourceLayout_FilterChips.js.map +1 -1
- package/dist/esm/components/resources/ResourceListItemRenderer.js +1 -1
- package/dist/esm/components/resources/ResourceListItemRenderer.js.map +1 -1
- package/dist/esm/components/resources/skeleton/ResourceHeaderTitle.d.ts +12 -0
- package/dist/esm/components/resources/skeleton/ResourceHeaderTitle.d.ts.map +1 -1
- package/dist/esm/components/resources/skeleton/ResourceHeaderTitle.js +13 -1
- package/dist/esm/components/resources/skeleton/ResourceHeaderTitle.js.map +1 -1
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.d.ts +106 -49
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.d.ts.map +1 -1
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.js +311 -109
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.js.map +1 -1
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.labels.d.ts.map +1 -1
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.labels.js +32 -2
- package/dist/esm/components/settings/AppPage_ConnectedAccountsSettings.labels.js.map +1 -1
- package/dist/esm/components/settings/SettingsHub.d.ts.map +1 -1
- package/dist/esm/components/settings/SettingsHub.js +30 -6
- package/dist/esm/components/settings/SettingsHub.js.map +1 -1
- package/dist/esm/components/views/onboarding-view/OnboardingView.d.ts.map +1 -1
- package/dist/esm/components/views/onboarding-view/OnboardingView.js +9 -2
- package/dist/esm/components/views/onboarding-view/OnboardingView.js.map +1 -1
- package/dist/esm/core/automated-layout/automated-layout.frontend.schemas.d.ts +16 -0
- package/dist/esm/core/automated-layout/automated-layout.frontend.schemas.d.ts.map +1 -1
- package/dist/esm/core/automated-layout/automated-layout.frontend.schemas.js +17 -0
- package/dist/esm/core/automated-layout/automated-layout.frontend.schemas.js.map +1 -1
- package/dist/esm/core/automated-layout/default-layout-generator.frontend.utils.d.ts.map +1 -1
- package/dist/esm/core/automated-layout/default-layout-generator.frontend.utils.js +3 -2
- package/dist/esm/core/automated-layout/default-layout-generator.frontend.utils.js.map +1 -1
- package/dist/esm/core/billing/billing-launcher.js +1 -1
- package/dist/esm/core/billing/billing-launcher.js.map +1 -1
- package/dist/esm/core/components-registry/provider-component-registry.frontend.service.d.ts +64 -4
- package/dist/esm/core/components-registry/provider-component-registry.frontend.service.d.ts.map +1 -1
- package/dist/esm/core/components-registry/provider-component-registry.frontend.service.js +76 -4
- package/dist/esm/core/components-registry/provider-component-registry.frontend.service.js.map +1 -1
- package/dist/esm/core/components-registry/useProviderComponent.frontend.hooks.d.ts +8 -0
- package/dist/esm/core/components-registry/useProviderComponent.frontend.hooks.d.ts.map +1 -1
- package/dist/esm/core/components-registry/useProviderComponent.frontend.hooks.js +1 -0
- package/dist/esm/core/components-registry/useProviderComponent.frontend.hooks.js.map +1 -1
- package/dist/esm/core/config/chart-view.frontend.schemas.d.ts +8 -8
- package/dist/esm/core/config/composite-view.schemas.d.ts +4 -4
- package/dist/esm/core/config/engine-settings-destinations.d.ts +0 -69
- package/dist/esm/core/config/engine-settings-destinations.d.ts.map +1 -1
- package/dist/esm/core/config/engine-settings-destinations.js +35 -74
- package/dist/esm/core/config/engine-settings-destinations.js.map +1 -1
- package/dist/esm/core/config/settings-hub.frontend.schemas.d.ts +10 -1
- package/dist/esm/core/config/settings-hub.frontend.schemas.d.ts.map +1 -1
- package/dist/esm/core/config/settings-hub.frontend.schemas.js +9 -0
- package/dist/esm/core/config/settings-hub.frontend.schemas.js.map +1 -1
- package/dist/esm/core/config/useSettingsHubConfig.d.ts.map +1 -1
- package/dist/esm/core/config/useSettingsHubConfig.js +8 -1
- package/dist/esm/core/config/useSettingsHubConfig.js.map +1 -1
- package/dist/esm/core/events/events.frontend.schemas.d.ts +4 -4
- package/dist/esm/core/external-providers/ProviderFilePickerHost.d.ts +12 -1
- package/dist/esm/core/external-providers/ProviderFilePickerHost.d.ts.map +1 -1
- package/dist/esm/core/external-providers/ProviderFilePickerHost.js +15 -2
- package/dist/esm/core/external-providers/ProviderFilePickerHost.js.map +1 -1
- package/dist/esm/core/external-providers/provider-file-selection.frontend.utils.d.ts +1 -2
- package/dist/esm/core/external-providers/provider-file-selection.frontend.utils.d.ts.map +1 -1
- package/dist/esm/core/external-providers/provider-file-selection.frontend.utils.js.map +1 -1
- package/dist/esm/core/flows-actors/FlowsActorsContext.d.ts +6 -1
- package/dist/esm/core/flows-actors/FlowsActorsContext.d.ts.map +1 -1
- package/dist/esm/core/flows-actors/FlowsActorsContext.js +10 -2
- package/dist/esm/core/flows-actors/FlowsActorsContext.js.map +1 -1
- package/dist/esm/core/i18n/I18nContext.d.ts +23 -0
- package/dist/esm/core/i18n/I18nContext.d.ts.map +1 -1
- package/dist/esm/core/i18n/I18nContext.js +6 -1
- package/dist/esm/core/i18n/I18nContext.js.map +1 -1
- package/dist/esm/core/requests/wildo-http-client-manual-calls.frontend.service.d.ts +75 -2
- package/dist/esm/core/requests/wildo-http-client-manual-calls.frontend.service.d.ts.map +1 -1
- package/dist/esm/core/requests/wildo-http-client-manual-calls.frontend.service.js +52 -0
- package/dist/esm/core/requests/wildo-http-client-manual-calls.frontend.service.js.map +1 -1
- package/dist/esm/core/requests/wildo-http-client-resources.frontend.service.d.ts +3 -2
- package/dist/esm/core/requests/wildo-http-client-resources.frontend.service.d.ts.map +1 -1
- package/dist/esm/core/requests/wildo-http-client-resources.frontend.service.js +57 -4
- package/dist/esm/core/requests/wildo-http-client-resources.frontend.service.js.map +1 -1
- package/dist/esm/core/resources/ResourceReadCacheBridge.d.ts.map +1 -1
- package/dist/esm/core/resources/ResourceReadCacheBridge.js +130 -37
- package/dist/esm/core/resources/ResourceReadCacheBridge.js.map +1 -1
- package/dist/esm/core/resources/resources.frontend.schemas.d.ts +2 -2
- package/dist/esm/core/runtime/start-application.frontend.service.d.ts.map +1 -1
- package/dist/esm/core/runtime/start-application.frontend.service.js +17 -9
- package/dist/esm/core/runtime/start-application.frontend.service.js.map +1 -1
- package/dist/esm/core/security/UserOrgsContext.d.ts.map +1 -1
- package/dist/esm/core/security/UserOrgsContext.js +61 -5
- package/dist/esm/core/security/UserOrgsContext.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -70,7 +70,7 @@ export declare function DropdownMenuSeparator({ preset, ...props }: DropdownMenu
|
|
|
70
70
|
export declare function DropdownMenuShortcut({ preset, ...props }: DropdownMenuShortcutProps & ComponentPresetProps): React.JSX.Element;
|
|
71
71
|
export declare function DropdownMenuSub({ preset, ...props }: DropdownMenuSubProps & ComponentPresetProps): React.JSX.Element;
|
|
72
72
|
export declare function DropdownMenuSubTrigger({ preset, ...props }: DropdownMenuSubTriggerProps & ComponentPresetProps): React.JSX.Element;
|
|
73
|
-
export declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & Pick<FloatingMenuContentBaseProps, "
|
|
73
|
+
export declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & Pick<FloatingMenuContentBaseProps, "sideOffset" | "alignOffset" | "avoidCollisions" | "collisionPadding" | "arrowPadding" | "loop"> & {
|
|
74
74
|
asChild?: boolean;
|
|
75
75
|
forceMount?: boolean;
|
|
76
76
|
onEscapeKeyDown?: (event: KeyboardEvent) => void;
|
|
@@ -85,7 +85,7 @@ export const ResourceLayout_FilterChips = ({ resourceType, filterFields, current
|
|
|
85
85
|
else if (field.fieldType === 'boolean') {
|
|
86
86
|
displayValue = value ? tc('booleanYes') : tc('booleanNo');
|
|
87
87
|
}
|
|
88
|
-
return (_jsxs("span", { className: "inline-flex items-center gap-1 ps-element-sm pe-1 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground", children: [_jsxs("span", { className: "text-muted-foreground
|
|
88
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1 ps-element-sm pe-1 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground", children: [_jsxs("span", { className: "text-muted-foreground", children: [label, ":"] }), displayValue, _jsx(Button, { type: "button", variant: ButtonVariants_Variant.GHOST, size: ButtonSize.ICON_SM, onClick: () => handleRemoveFilter(key), className: "ms-0.5 h-4 w-4 p-0", "aria-label": tc('removeFilter'), children: removeIcon })] }, key));
|
|
89
89
|
}), _jsx(Button, { type: "button", variant: ButtonVariants_Variant.GHOST, size: ButtonSize.SM, onClick: handleClearAll, className: "text-xs text-muted-foreground", children: tc('clearAll') })] }));
|
|
90
90
|
};
|
|
91
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceLayout_FilterChips.js","sourceRoot":"","sources":["../../../../src/components/resources/ResourceLayout_FilterChips.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * ResourceLayout_FilterChips — the active-filter summary row for a collection.\n *\n * Renders one removable chip per active filter (with the resolved label/value —\n * FK chips resolve the target resource's display label, never a raw id) plus a\n * \"Clear all\" action. Hidden entirely when no filters are active.\n *\n * Split out of `ResourceLayout_FiltersControl` so the collection toolbar can host\n * the compact Filters TRIGGER while the chips occupy their own row below the\n * toolbar — keeping the toolbar itself uncluttered. `FiltersControl` re-uses this\n * component for its legacy inline placement.\n */\n\nimport React, { useCallback, useMemo } from 'react';\nimport { useI18n } from '~wildo-frontend-lib/core/i18n/useI18n';\nimport { cn } from '~wildo-frontend-lib/core/ui/cn';\nimport { useIconRegistry } from '~wildo-frontend-lib/core/icons/useIconRegistry';\nimport { StandardIconReference, IconSize } from '~wildo-frontend-lib/core/icons/icon-registry.frontend.schemas';\nimport { Button, ButtonVariants_Variant } from '~wildo-frontend-lib/components/actions/button';\nimport { useResourceSearch } from '~wildo-frontend-lib/components/resources-fields/resource-reference/useResourceSearch';\nimport { FrontendComponentType, ButtonSize } from '@wildo-ai/presets-components-models';\nimport type { FilterFieldDefinition, FilterValues } from './ResourceLayout_FiltersControl';\n\nexport interface ResourceLayout_FilterChipsProps {\n /** Resource type for i18n label resolution. */\n resourceType: string;\n /** Filter field definitions (drives per-field type → chip value formatting). */\n filterFields: readonly FilterFieldDefinition[];\n /** Current active filters. */\n currentFilters: FilterValues;\n /** Callback when filters change (used to remove one / clear all). */\n onFiltersChange: (filters: FilterValues) => void;\n /** Additional CSS classes. */\n className?: string;\n}\n\nexport const ResourceLayout_FilterChips = ({\n resourceType,\n filterFields,\n currentFilters,\n onFiltersChange,\n className,\n}: ResourceLayout_FilterChipsProps): React.JSX.Element | null => {\n const { tResourceFieldDisplay, tResourceFieldEnumValue, tComponent, isReady } = useI18n({\n component: FrontendComponentType.RESOURCES_LAYOUTS_FILTERS,\n resource: resourceType,\n });\n const { renderIcon } = useIconRegistry();\n\n const tc = (key: string): string => tComponent(FrontendComponentType.RESOURCES_LAYOUTS_FILTERS, key);\n\n // Count/render ONLY the fields this row is responsible for — the panel's\n // `filterFields`. A collection's `currentFilters` also carries the PROMOTED\n // quick-filter field's value (shared filter state, deliberately excluded from\n // `filterFields` so the tab strip owns that field's active-state display).\n // Iterating `currentFilters` here surfaced that field as a redundant chip whose\n // label/value could not be resolved (no field definition ⇒ raw \"status:completed\"),\n // and disagreed with the (correctly scoped) Filters badge. Stay in lockstep with\n // the panel and the badge: a chip renders iff the field is a panel field.\n const activeFilterFields = useMemo(\n () => filterFields.filter(f => {\n const v = currentFilters[f.fieldName];\n return v !== undefined && v !== null && v !== '';\n }),\n [filterFields, currentFilters],\n );\n\n const fieldLabels = useMemo(() => {\n const labels: Record<string, string> = {};\n for (const field of filterFields) {\n const label = tResourceFieldDisplay(resourceType, field.fieldName, 'label');\n labels[field.fieldName] = typeof label === 'string' ? label : field.fieldName;\n }\n return labels;\n }, [filterFields, resourceType, tResourceFieldDisplay]);\n\n // Clear ONLY the panel's own fields; PRESERVE any filter value this row does not\n // represent — the promoted quick-filter field, whose tab strip owns its reset via\n // its own \"All\" segment. \"Clear all\" is a chips/panel affordance, so it must be\n // symmetric with the panel, chips, and count, which all scope to `filterFields`;\n // reaching into the promoted field would silently reset a separate, still-visible\n // control the chips row never showed — the mutation-side mirror of the count/chips\n // display leak. (Industry precedent: Shopify/Notion clear the filter bar without\n // changing the selected view/status tab.) For the self-contained callers whose\n // values already align 1:1 with `filterFields`, this still clears everything.\n const handleClearAll = useCallback(() => {\n const next = { ...currentFilters };\n for (const field of filterFields) delete next[field.fieldName];\n onFiltersChange(next);\n }, [currentFilters, filterFields, onFiltersChange]);\n const handleRemoveFilter = useCallback((fieldName: string) => {\n const next = { ...currentFilters };\n delete next[fieldName];\n onFiltersChange(next);\n }, [currentFilters, onFiltersChange]);\n\n if (!isReady || activeFilterFields.length === 0) return null;\n\n const removeIcon = renderIcon(StandardIconReference.X_MARK, { size: IconSize.XS });\n\n return (\n <div className={cn('flex items-center gap-element-sm flex-wrap', className)}>\n {activeFilterFields.map((field) => {\n const key = field.fieldName;\n const value = currentFilters[key];\n const label = fieldLabels[key] || key;\n\n let displayValue: React.ReactNode = String(value);\n if (field.fieldType === 'reference') {\n displayValue = <ReferenceFilterChipValue field={field} value={String(value)} />;\n } else if (field.fieldType === 'enum') {\n const enumLabel = tResourceFieldEnumValue(resourceType, key, String(value));\n if (typeof enumLabel === 'string') displayValue = enumLabel;\n } else if (field.fieldType === 'boolean') {\n displayValue = value ? tc('booleanYes') : tc('booleanNo');\n }\n\n return (\n <span\n key={key}\n className=\"inline-flex items-center gap-1 ps-element-sm pe-1 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground\"\n >\n <span className=\"text-muted-foreground/70\">{label}:</span>\n {displayValue}\n <Button\n type=\"button\"\n variant={ButtonVariants_Variant.GHOST}\n size={ButtonSize.ICON_SM}\n onClick={() => handleRemoveFilter(key)}\n className=\"ms-0.5 h-4 w-4 p-0\"\n aria-label={tc('removeFilter')}\n >\n {removeIcon}\n </Button>\n </span>\n );\n })}\n <Button\n type=\"button\"\n variant={ButtonVariants_Variant.GHOST}\n size={ButtonSize.SM}\n onClick={handleClearAll}\n className=\"text-xs text-muted-foreground\"\n >\n {tc('clearAll')}\n </Button>\n </div>\n );\n};\n\n// =============================================================================\n// REFERENCE FILTER CHIP VALUE — resolves an FK id to its display label\n// =============================================================================\n\ninterface ReferenceFilterChipValueProps {\n field: FilterFieldDefinition;\n value: string;\n}\n\n/**\n * Chip text for an active reference filter. Reuses `useResourceSearch`'s\n * current-value label resolution (READ on the target resource +\n * `getFrontendResourceLabel`); the only network activity is the one label READ,\n * cached for the chip's lifetime (a chip never opens the dropdown).\n */\nconst ReferenceFilterChipValue: React.FC<ReferenceFilterChipValueProps> = ({ field, value }) => {\n const { getDisplayLabel, isLoadingCurrentValue } = useResourceSearch({\n schema: field.schema,\n name: field.fieldName,\n value,\n targetResourceType: field.referenceTargetResourceType,\n });\n\n if (isLoadingCurrentValue) return <>…</>;\n return <>{getDisplayLabel(value)}</>;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"ResourceLayout_FilterChips.js","sourceRoot":"","sources":["../../../../src/components/resources/ResourceLayout_FilterChips.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * ResourceLayout_FilterChips — the active-filter summary row for a collection.\n *\n * Renders one removable chip per active filter (with the resolved label/value —\n * FK chips resolve the target resource's display label, never a raw id) plus a\n * \"Clear all\" action. Hidden entirely when no filters are active.\n *\n * Split out of `ResourceLayout_FiltersControl` so the collection toolbar can host\n * the compact Filters TRIGGER while the chips occupy their own row below the\n * toolbar — keeping the toolbar itself uncluttered. `FiltersControl` re-uses this\n * component for its legacy inline placement.\n */\n\nimport React, { useCallback, useMemo } from 'react';\nimport { useI18n } from '~wildo-frontend-lib/core/i18n/useI18n';\nimport { cn } from '~wildo-frontend-lib/core/ui/cn';\nimport { useIconRegistry } from '~wildo-frontend-lib/core/icons/useIconRegistry';\nimport { StandardIconReference, IconSize } from '~wildo-frontend-lib/core/icons/icon-registry.frontend.schemas';\nimport { Button, ButtonVariants_Variant } from '~wildo-frontend-lib/components/actions/button';\nimport { useResourceSearch } from '~wildo-frontend-lib/components/resources-fields/resource-reference/useResourceSearch';\nimport { FrontendComponentType, ButtonSize } from '@wildo-ai/presets-components-models';\nimport type { FilterFieldDefinition, FilterValues } from './ResourceLayout_FiltersControl';\n\nexport interface ResourceLayout_FilterChipsProps {\n /** Resource type for i18n label resolution. */\n resourceType: string;\n /** Filter field definitions (drives per-field type → chip value formatting). */\n filterFields: readonly FilterFieldDefinition[];\n /** Current active filters. */\n currentFilters: FilterValues;\n /** Callback when filters change (used to remove one / clear all). */\n onFiltersChange: (filters: FilterValues) => void;\n /** Additional CSS classes. */\n className?: string;\n}\n\nexport const ResourceLayout_FilterChips = ({\n resourceType,\n filterFields,\n currentFilters,\n onFiltersChange,\n className,\n}: ResourceLayout_FilterChipsProps): React.JSX.Element | null => {\n const { tResourceFieldDisplay, tResourceFieldEnumValue, tComponent, isReady } = useI18n({\n component: FrontendComponentType.RESOURCES_LAYOUTS_FILTERS,\n resource: resourceType,\n });\n const { renderIcon } = useIconRegistry();\n\n const tc = (key: string): string => tComponent(FrontendComponentType.RESOURCES_LAYOUTS_FILTERS, key);\n\n // Count/render ONLY the fields this row is responsible for — the panel's\n // `filterFields`. A collection's `currentFilters` also carries the PROMOTED\n // quick-filter field's value (shared filter state, deliberately excluded from\n // `filterFields` so the tab strip owns that field's active-state display).\n // Iterating `currentFilters` here surfaced that field as a redundant chip whose\n // label/value could not be resolved (no field definition ⇒ raw \"status:completed\"),\n // and disagreed with the (correctly scoped) Filters badge. Stay in lockstep with\n // the panel and the badge: a chip renders iff the field is a panel field.\n const activeFilterFields = useMemo(\n () => filterFields.filter(f => {\n const v = currentFilters[f.fieldName];\n return v !== undefined && v !== null && v !== '';\n }),\n [filterFields, currentFilters],\n );\n\n const fieldLabels = useMemo(() => {\n const labels: Record<string, string> = {};\n for (const field of filterFields) {\n const label = tResourceFieldDisplay(resourceType, field.fieldName, 'label');\n labels[field.fieldName] = typeof label === 'string' ? label : field.fieldName;\n }\n return labels;\n }, [filterFields, resourceType, tResourceFieldDisplay]);\n\n // Clear ONLY the panel's own fields; PRESERVE any filter value this row does not\n // represent — the promoted quick-filter field, whose tab strip owns its reset via\n // its own \"All\" segment. \"Clear all\" is a chips/panel affordance, so it must be\n // symmetric with the panel, chips, and count, which all scope to `filterFields`;\n // reaching into the promoted field would silently reset a separate, still-visible\n // control the chips row never showed — the mutation-side mirror of the count/chips\n // display leak. (Industry precedent: Shopify/Notion clear the filter bar without\n // changing the selected view/status tab.) For the self-contained callers whose\n // values already align 1:1 with `filterFields`, this still clears everything.\n const handleClearAll = useCallback(() => {\n const next = { ...currentFilters };\n for (const field of filterFields) delete next[field.fieldName];\n onFiltersChange(next);\n }, [currentFilters, filterFields, onFiltersChange]);\n const handleRemoveFilter = useCallback((fieldName: string) => {\n const next = { ...currentFilters };\n delete next[fieldName];\n onFiltersChange(next);\n }, [currentFilters, onFiltersChange]);\n\n if (!isReady || activeFilterFields.length === 0) return null;\n\n const removeIcon = renderIcon(StandardIconReference.X_MARK, { size: IconSize.XS });\n\n return (\n <div className={cn('flex items-center gap-element-sm flex-wrap', className)}>\n {activeFilterFields.map((field) => {\n const key = field.fieldName;\n const value = currentFilters[key];\n const label = fieldLabels[key] || key;\n\n let displayValue: React.ReactNode = String(value);\n if (field.fieldType === 'reference') {\n displayValue = <ReferenceFilterChipValue field={field} value={String(value)} />;\n } else if (field.fieldType === 'enum') {\n const enumLabel = tResourceFieldEnumValue(resourceType, key, String(value));\n if (typeof enumLabel === 'string') displayValue = enumLabel;\n } else if (field.fieldType === 'boolean') {\n displayValue = value ? tc('booleanYes') : tc('booleanNo');\n }\n\n return (\n <span\n key={key}\n className=\"inline-flex items-center gap-1 ps-element-sm pe-1 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground\"\n >\n <span className=\"text-muted-foreground\">{label}:</span>\n {displayValue}\n <Button\n type=\"button\"\n variant={ButtonVariants_Variant.GHOST}\n size={ButtonSize.ICON_SM}\n onClick={() => handleRemoveFilter(key)}\n className=\"ms-0.5 h-4 w-4 p-0\"\n aria-label={tc('removeFilter')}\n >\n {removeIcon}\n </Button>\n </span>\n );\n })}\n <Button\n type=\"button\"\n variant={ButtonVariants_Variant.GHOST}\n size={ButtonSize.SM}\n onClick={handleClearAll}\n className=\"text-xs text-muted-foreground\"\n >\n {tc('clearAll')}\n </Button>\n </div>\n );\n};\n\n// =============================================================================\n// REFERENCE FILTER CHIP VALUE — resolves an FK id to its display label\n// =============================================================================\n\ninterface ReferenceFilterChipValueProps {\n field: FilterFieldDefinition;\n value: string;\n}\n\n/**\n * Chip text for an active reference filter. Reuses `useResourceSearch`'s\n * current-value label resolution (READ on the target resource +\n * `getFrontendResourceLabel`); the only network activity is the one label READ,\n * cached for the chip's lifetime (a chip never opens the dropdown).\n */\nconst ReferenceFilterChipValue: React.FC<ReferenceFilterChipValueProps> = ({ field, value }) => {\n const { getDisplayLabel, isLoadingCurrentValue } = useResourceSearch({\n schema: field.schema,\n name: field.fieldName,\n value,\n targetResourceType: field.referenceTargetResourceType,\n });\n\n if (isLoadingCurrentValue) return <>…</>;\n return <>{getDisplayLabel(value)}</>;\n};\n"]}
|
|
@@ -58,7 +58,7 @@ export function FieldValueRenderer({ fieldName, value, fieldSchema, fieldLabel,
|
|
|
58
58
|
}, [fieldSchema, resourceType, fieldName, tResourceFieldEnumValue]);
|
|
59
59
|
if (value === null || value === undefined)
|
|
60
60
|
return null;
|
|
61
|
-
return (_jsxs("div", { className: "flex min-w-0 items-baseline gap-element-sm", children: [fieldLabel && _jsxs("span", { className: "text-xs text-muted-foreground
|
|
61
|
+
return (_jsxs("div", { className: "flex min-w-0 items-baseline gap-element-sm", children: [fieldLabel && _jsxs("span", { className: "text-xs text-muted-foreground", children: [fieldLabel, ":"] }), fieldSchema ? (
|
|
62
62
|
// COMPACT chrome → bare value with the full decorator policy applied.
|
|
63
63
|
_jsx(DisplayChromeProvider, { displayChromeMode: DisplayChromeMode.COMPACT, children: _jsx(DisplayField, { name: fieldName, schema: fieldSchema, value: value, enumLabels: enumLabels, disableInlineEdit: true, className: "min-w-0 truncate text-sm text-muted-foreground" }) })) : (
|
|
64
64
|
// No schema resolved for this field — `DisplayField` requires a schema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceListItemRenderer.js","sourceRoot":"","sources":["../../../../src/components/resources/ResourceListItemRenderer.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * ResourceListItemRenderer\n *\n * Shared item renderer for List and Search presets.\n * Encapsulates schema-aware field rendering (FK, enum, date, array),\n * primary/secondary field split, and per-item operations.\n *\n * Used by List_Default and Search_Default to ensure parity in how\n * resource items are displayed.\n */\n\nimport React, { useMemo, useCallback } from 'react';\nimport { z } from 'zod';\nimport {\n CoreResourceOperation,\n ResourceOperationDisplayMode,\n ErrorCustomMessageReference,\n} from '@wildo-ai/saas-models';\nimport type { AnyFrontendResourceConfiguration, FrontendResourcesRegistry } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { getSchemaType, FieldSchemaType, getEnumValues, DisplayChromeMode, ViewportBreakpoint } from '@wildo-ai/zod-decorators';\nimport { UIContext } from '~wildo-frontend-lib/core/ui/UIContext';\nimport { useLongPress } from '~wildo-frontend-lib/components/resources/useLongPress';\nimport { DisplayField } from '~wildo-frontend-lib/components/data-display/DisplayField';\nimport { DisplayChromeProvider } from '~wildo-frontend-lib/components/data-display/chromes/DisplayChromeContext';\nimport { ResourceSummaryDisplayer } from '~wildo-frontend-lib/components/data-display/ResourceSummaryDisplayer';\nimport { Text } from '~wildo-frontend-lib/components/typography/text';\nimport { FrontendResourceUtils, frontendResourceUtils } from '~wildo-frontend-lib/core/resources/frontend-resource.utils';\nimport { AutomatedLayoutFrontendUtils } from '~wildo-frontend-lib/core/automated-layout/automated-layout.frontend.utils';\nimport type { FrontendResourceOperationContext } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { cn } from '~wildo-frontend-lib/core/ui/cn';\nimport { CheckboxRoot } from '~wildo-frontend-lib/components/form/controls/boolean/checkbox';\nimport type { CardActionPlacement } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { resolveCardActionPlacementClasses } from './card-action-placement.utils';\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ResourceListItemRendererProps {\n /** The data item to render */\n item: Record<string, unknown>;\n /** Resource type */\n resourceType: string;\n /** Resource configuration */\n resourceConfig: AnyFrontendResourceConfiguration;\n /** Resource registry */\n registry: FrontendResourcesRegistry;\n /** Ordered list of summary fields to display */\n summaryFields: string[];\n /** Pre-resolved field labels (from i18n) */\n fieldLabelsMap: Record<string, string>;\n /** Function to resolve enum display values via i18n */\n tResourceFieldEnumValue: (resourceType: string, fieldName: string, enumValue: string) => string;\n /** Click handler when the item row is clicked */\n onItemClick: (item: Record<string, unknown>) => void;\n /** Optional operations component to render for this item */\n operationsSlot?: React.ReactNode;\n /**\n * Where the per-item operations sit within the card (2-axis anchor +\n * orientation). Resolved by the host from `collectionDisplayConfig.cardActionPlacement`.\n * Omitted → the default (inline-end, vertically centered, horizontal row).\n */\n cardActionPlacement?: CardActionPlacement;\n /**\n * Selection affordance. When `selectable`, a leading checkbox renders and\n * toggles selection via `onToggleSelect` (independent of the row click, which\n * still navigates). `isSelected` drives the checkbox + a selected highlight.\n */\n selectable?: boolean;\n isSelected?: boolean;\n onToggleSelect?: (item: Record<string, unknown>) => void;\n /**\n * Whether the collection is currently in SELECTION MODE (any item selected).\n * Mobile UX: a long-press enters selection mode, and while active a plain TAP\n * toggles selection instead of navigating (the \"long-press to multi-select\"\n * pattern). Gated to touch/mobile — on desktop a click always navigates and\n * the checkbox is the explicit toggle.\n */\n selectionActive?: boolean;\n /** Accessible label for the selection checkbox (host provides the i18n string). */\n selectAriaLabel?: string;\n /** Optional className for the root container */\n className?: string;\n /** Interactive CSS classes from design system (hover state, transition) */\n interactiveClasses?: {\n hover?: string;\n transition?: string;\n };\n}\n\n// =============================================================================\n// FIELD VALUE RENDERER\n// =============================================================================\n\nexport interface FieldValueRendererProps {\n fieldName: string;\n value: unknown;\n fieldSchema: z.ZodTypeAny | undefined;\n fieldLabel: string | undefined;\n resourceType: string;\n tResourceFieldEnumValue: (resourceType: string, fieldName: string, enumValue: string) => string;\n}\n\n/**\n * Renders a single summary field VALUE through the shared decorator-driven\n * display pipeline (`DisplayField` → `DisplayValueDispatcher` → presets) — the\n * SAME path the detail/read view uses — so the FULL zod-UI display policy is\n * honored in lists/tables by default: enum → badge (+ per-value colors),\n * boolean → checkmark / yes-no, string → truncate / multiline, FK → reference,\n * date → formatted, money/bigdecimal/duration/etc.\n *\n * Previously this re-implemented a weak subset (enum-as-plain-text, naive array\n * join, `String(value)` fallback) that silently ignored every\n * `{primitive}UI().display` decorator, so listings/tables diverged from the\n * detail view (e.g. a `displayMode: BADGE` enum rendered as plain text). Routing\n * through `DisplayField` removes that divergence and the maintenance burden of a\n * parallel renderer.\n *\n * The card (\"Label:\" prefix) and the table (column header) own the label, so the\n * value is rendered under COMPACT chrome — a bare value with no framework\n * label/description wrapper. i18n enum labels are threaded explicitly via\n * `enumLabels` because there is no surrounding `DisplayTemplateContext`\n * (which is what supplies them inside `AutomaticDisplayer`).\n */\nexport function FieldValueRenderer({\n fieldName,\n value,\n fieldSchema,\n fieldLabel,\n resourceType,\n tResourceFieldEnumValue,\n}: FieldValueRendererProps): React.JSX.Element | null {\n // Build the enum value→label map from the same i18n translator the list\n // already uses, so enum badges show translated labels rather than raw enum\n // values. Hook runs unconditionally, before the null-value early return.\n const enumLabels = useMemo<Record<string, string> | undefined>(() => {\n if (!fieldSchema || getSchemaType(fieldSchema) !== FieldSchemaType.ENUM) return undefined;\n const enumValues = getEnumValues(fieldSchema);\n if (enumValues.length === 0) return undefined;\n return Object.fromEntries(\n enumValues.map((enumValue) => [enumValue, tResourceFieldEnumValue(resourceType, fieldName, enumValue)]),\n );\n }, [fieldSchema, resourceType, fieldName, tResourceFieldEnumValue]);\n\n if (value === null || value === undefined) return null;\n\n return (\n <div className=\"flex min-w-0 items-baseline gap-element-sm\">\n {fieldLabel && <span className=\"text-xs text-muted-foreground/70\">{fieldLabel}:</span>}\n {fieldSchema ? (\n // COMPACT chrome → bare value with the full decorator policy applied.\n <DisplayChromeProvider displayChromeMode={DisplayChromeMode.COMPACT}>\n <DisplayField\n name={fieldName}\n schema={fieldSchema}\n value={value}\n enumLabels={enumLabels}\n disableInlineEdit\n className=\"min-w-0 truncate text-sm text-muted-foreground\"\n />\n </DisplayChromeProvider>\n ) : (\n // No schema resolved for this field — `DisplayField` requires a schema,\n // so fall back to a plain string (matches the previous default branch).\n <span className=\"min-w-0 truncate text-sm text-muted-foreground\">{String(value)}</span>\n )}\n </div>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\nexport function ResourceListItemRenderer({\n item,\n resourceType,\n resourceConfig,\n registry,\n summaryFields,\n fieldLabelsMap,\n tResourceFieldEnumValue,\n onItemClick,\n operationsSlot,\n cardActionPlacement,\n selectable = false,\n isSelected = false,\n onToggleSelect,\n selectionActive = false,\n selectAriaLabel,\n className,\n interactiveClasses,\n}: ResourceListItemRendererProps): React.JSX.Element {\n if (!resourceConfig?.resourceFieldIdentifier) {\n throw FrontendResourceUtils.buildConfigurationError(\n ErrorCustomMessageReference.FRONTEND_RESOURCE_FIELD_IDENTIFIER_NOT_FOUND,\n { resourceType, component: 'ResourceListItemRenderer' }\n );\n }\n const fieldIdentifier = resourceConfig.resourceFieldIdentifier as string;\n // Resolve the record's own id (`_id`), not the cross-resource field-identifier\n // name (`<resource>Id`) which is absent from a plain record — see\n // FrontendResourceUtils.getResourceObjectId.\n const itemId = FrontendResourceUtils.getResourceObjectId(item, fieldIdentifier);\n\n // Primary display: use computeFrontendResourceLabel for the primary value.\n // For a junction this resolves the server-projected instance label (e.g. a\n // `forwardedLabel` virtual field referenced by `getFrontendResourceLabel`).\n const primaryLabel = useMemo((): string => {\n const label = FrontendResourceUtils.computeFrontendResourceLabel({\n resourceType,\n registry,\n currentObject: item,\n });\n return String(label || item[summaryFields[0]] || `${resourceType} #${itemId}`);\n }, [item, resourceType, registry, summaryFields, itemId]);\n\n // Secondary fields: every summary field EXCEPT the one already shown as the\n // primary label (title). The title comes from `computeFrontendResourceLabel`\n // — an arbitrary label strategy (a `string[]` join or a function), NOT\n // necessarily `summaryFields[0]`. Excluding the title positionally (`slice(1)`)\n // had two bugs: (a) it REPEATED the title when the label field wasn't first\n // (e.g. a list whose label is `name` but whose first summary field is `status`\n // → \"Name: …\" rendered under the identical title), and (b) it silently DROPPED\n // `summaryFields[0]` whenever that field wasn't the label. Excluding the title\n // by VALUE fixes both: the field whose value equals the title is the label\n // field wherever it sits, and every genuine non-label summary field is kept.\n // (The primary-scope FK is dropped upstream by\n // `ResourceLayoutUtils.detectSummaryFields`, so it never reaches here.)\n const secondaryFields = useMemo(\n () => summaryFields.filter((fieldName) => {\n const value = item[fieldName];\n if (value === null || value === undefined) return false;\n return String(value) !== primaryLabel;\n }),\n [summaryFields, item, primaryLabel],\n );\n\n // Schema shape for field type detection\n const schemaShape = useMemo(() => {\n const mainSchema = resourceConfig?.mainSchema;\n if (!mainSchema) return null;\n // Polymorphic resources feed a discriminated-union schema; resolve the union-aware field shape (else getObjectShape returns null for the union → no field labels).\n return AutomatedLayoutFrontendUtils.getObjectLikeFieldShape(mainSchema, { commonOnly: false }) ?? null;\n }, [resourceConfig]);\n\n // AUTHORED summary template — the `layout.summary` contract promises coverage\n // of \"cards, list items, FK references\". When a resource authors one, the card\n // defers its ENTIRE content region (title + body) to `ResourceSummaryDisplayer`:\n // the title is now a first-class template element (`<SummaryTitle />`, placed by\n // `<SummaryLayout>`), so a status badge can sit inline with the title and the\n // exact same composition renders in the FK popover. We pass the primary label we\n // already computed (correct `t`, junction forwardedLabel) into the displayer's\n // SummaryContext instead of rendering our own title above it.\n //\n // Resources WITHOUT an authored template keep the legacy host-owned title +\n // generic \"Label: value\" body: routing them through the GENERATED default\n // template would restyle every list in every app for no authored reason.\n const authoredSummaryTemplate = resourceConfig?.frontendConfiguration?.layout?.summary;\n\n const hoverClass = interactiveClasses?.hover ?? '';\n const transitionClass = interactiveClasses?.transition ?? '';\n // Resolve where the per-item actions sit (2-axis anchor → flex classes). The\n // content + actions share one flex container so actions never overlap text.\n const placement = resolveCardActionPlacementClasses(cardActionPlacement);\n\n // Mobile multi-select: a long-press ENTERS selection mode (selects this item);\n // while selection mode is active (`selectionActive`), a plain TAP toggles\n // selection instead of navigating. Gated to a mobile viewport — on desktop a\n // click always navigates and the checkbox is the explicit toggle. Long-press\n // is touch-only (the hook binds touch handlers).\n // Optional UIContext read: a rendering leaf shouldn't hard-require the shell's\n // UIProvider just to check the breakpoint — outside a provider (isolated\n // renders/tests) it degrades to desktop behavior (isMobile = false).\n const ui = React.useContext(UIContext);\n const isMobile = ui?.UIConfig?.currentBreakpoint === ViewportBreakpoint.MOBILE;\n const tapTogglesSelection = selectable && selectionActive && isMobile;\n // Long-press ENTERS selection mode — gated to the SAME condition as tap-toggle\n // (selectable + mobile viewport) so the two can't disagree (e.g. enter mode on\n // a tablet where tap-toggle is off, stranding the user mid-multiselect).\n const longPress = useLongPress(\n () => onToggleSelect?.(item),\n { enabled: selectable && !!onToggleSelect && isMobile },\n );\n\n const handleActivate = () => {\n // Swallow the synthetic click that follows a long-press (it already selected).\n if (longPress.consumeLongPress()) return;\n if (tapTogglesSelection) {\n onToggleSelect?.(item);\n return;\n }\n onItemClick(item);\n };\n\n return (\n <div\n className={cn(\n 'p-card', hoverClass, 'cursor-pointer', transitionClass,\n isSelected && 'bg-primary/5',\n // Prevent the browser text-selection that a long-press would start.\n selectable && 'select-none',\n className,\n )}\n onClick={handleActivate}\n role=\"button\"\n tabIndex={0}\n aria-label={String(primaryLabel)}\n {...longPress.handlers}\n // Selection state is conveyed by the row's checkbox (aria-selected is not\n // valid on role=\"button\"); the bg tint is a purely visual reinforcement.\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (tapTogglesSelection) {\n onToggleSelect?.(item);\n } else {\n onItemClick(item);\n }\n }\n }}\n >\n {/* Leading selection rail (only when the view is selectable). The checkbox\n toggles selection and stops propagation so it never triggers the row's\n navigation — mirrors the table select-cell contract. */}\n <div className=\"flex items-start gap-element-sm\">\n {/* stopPropagation on click, keydown AND touchstart: the row is a\n role=\"button\" with Enter/Space navigation and a touch long-press, so\n without these guards toggling the checkbox (tap/keyboard) would also\n navigate, and a hold over it would arm the row's long-press. */}\n {selectable && (\n <div\n className=\"shrink-0 pt-0.5\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n <CheckboxRoot\n checked={isSelected}\n onCheckedChange={() => onToggleSelect?.(item)}\n aria-label={selectAriaLabel ?? String(primaryLabel)}\n />\n </div>\n )}\n <div className={cn(placement.containerClass, 'min-w-0 flex-1')}>\n <div className={cn('space-y-item-xs', placement.contentClass)}>\n {authoredSummaryTemplate && resourceConfig?.mainSchema ? (\n // Authored template owns title + body. The title is fed via\n // SummaryContext (primaryLabel) and rendered by `<SummaryTitle />`\n // inside the template, so we do NOT render our own title here. The\n // region stays click-through so the whole card navigates (same as the\n // legacy body); an author who embeds a genuinely interactive control\n // stops propagation within their own template.\n <ResourceSummaryDisplayer\n resourceType={resourceType}\n schema={resourceConfig.mainSchema}\n data={item}\n primaryLabel={primaryLabel}\n />\n ) : (\n <>\n <Text as=\"p\" className=\"font-medium text-foreground\" truncate>{primaryLabel}</Text>\n {secondaryFields.map((fieldName) => {\n const value = item[fieldName];\n if (value === null || value === undefined) return null;\n return (\n <div key={fieldName}>\n <FieldValueRenderer\n fieldName={fieldName}\n value={value}\n fieldSchema={schemaShape?.[fieldName] as z.ZodTypeAny | undefined}\n fieldLabel={fieldLabelsMap[fieldName]}\n resourceType={resourceType}\n tResourceFieldEnumValue={tResourceFieldEnumValue}\n />\n </div>\n );\n })}\n </>\n )}\n </div>\n {operationsSlot && (\n // stopPropagation on click, keydown AND touchstart: an action tap or\n // keyboard activation must not also navigate the row, and a hold over\n // an action must not arm the row's long-press selection. (Mirrors the\n // checkbox rail — all three axes.)\n <div\n className={placement.actionsClass}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {operationsSlot}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ResourceListItemRenderer.js","sourceRoot":"","sources":["../../../../src/components/resources/ResourceListItemRenderer.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * ResourceListItemRenderer\n *\n * Shared item renderer for List and Search presets.\n * Encapsulates schema-aware field rendering (FK, enum, date, array),\n * primary/secondary field split, and per-item operations.\n *\n * Used by List_Default and Search_Default to ensure parity in how\n * resource items are displayed.\n */\n\nimport React, { useMemo, useCallback } from 'react';\nimport { z } from 'zod';\nimport {\n CoreResourceOperation,\n ResourceOperationDisplayMode,\n ErrorCustomMessageReference,\n} from '@wildo-ai/saas-models';\nimport type { AnyFrontendResourceConfiguration, FrontendResourcesRegistry } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { getSchemaType, FieldSchemaType, getEnumValues, DisplayChromeMode, ViewportBreakpoint } from '@wildo-ai/zod-decorators';\nimport { UIContext } from '~wildo-frontend-lib/core/ui/UIContext';\nimport { useLongPress } from '~wildo-frontend-lib/components/resources/useLongPress';\nimport { DisplayField } from '~wildo-frontend-lib/components/data-display/DisplayField';\nimport { DisplayChromeProvider } from '~wildo-frontend-lib/components/data-display/chromes/DisplayChromeContext';\nimport { ResourceSummaryDisplayer } from '~wildo-frontend-lib/components/data-display/ResourceSummaryDisplayer';\nimport { Text } from '~wildo-frontend-lib/components/typography/text';\nimport { FrontendResourceUtils, frontendResourceUtils } from '~wildo-frontend-lib/core/resources/frontend-resource.utils';\nimport { AutomatedLayoutFrontendUtils } from '~wildo-frontend-lib/core/automated-layout/automated-layout.frontend.utils';\nimport type { FrontendResourceOperationContext } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { cn } from '~wildo-frontend-lib/core/ui/cn';\nimport { CheckboxRoot } from '~wildo-frontend-lib/components/form/controls/boolean/checkbox';\nimport type { CardActionPlacement } from '~wildo-frontend-lib/core/resources/resources.frontend.schemas';\nimport { resolveCardActionPlacementClasses } from './card-action-placement.utils';\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ResourceListItemRendererProps {\n /** The data item to render */\n item: Record<string, unknown>;\n /** Resource type */\n resourceType: string;\n /** Resource configuration */\n resourceConfig: AnyFrontendResourceConfiguration;\n /** Resource registry */\n registry: FrontendResourcesRegistry;\n /** Ordered list of summary fields to display */\n summaryFields: string[];\n /** Pre-resolved field labels (from i18n) */\n fieldLabelsMap: Record<string, string>;\n /** Function to resolve enum display values via i18n */\n tResourceFieldEnumValue: (resourceType: string, fieldName: string, enumValue: string) => string;\n /** Click handler when the item row is clicked */\n onItemClick: (item: Record<string, unknown>) => void;\n /** Optional operations component to render for this item */\n operationsSlot?: React.ReactNode;\n /**\n * Where the per-item operations sit within the card (2-axis anchor +\n * orientation). Resolved by the host from `collectionDisplayConfig.cardActionPlacement`.\n * Omitted → the default (inline-end, vertically centered, horizontal row).\n */\n cardActionPlacement?: CardActionPlacement;\n /**\n * Selection affordance. When `selectable`, a leading checkbox renders and\n * toggles selection via `onToggleSelect` (independent of the row click, which\n * still navigates). `isSelected` drives the checkbox + a selected highlight.\n */\n selectable?: boolean;\n isSelected?: boolean;\n onToggleSelect?: (item: Record<string, unknown>) => void;\n /**\n * Whether the collection is currently in SELECTION MODE (any item selected).\n * Mobile UX: a long-press enters selection mode, and while active a plain TAP\n * toggles selection instead of navigating (the \"long-press to multi-select\"\n * pattern). Gated to touch/mobile — on desktop a click always navigates and\n * the checkbox is the explicit toggle.\n */\n selectionActive?: boolean;\n /** Accessible label for the selection checkbox (host provides the i18n string). */\n selectAriaLabel?: string;\n /** Optional className for the root container */\n className?: string;\n /** Interactive CSS classes from design system (hover state, transition) */\n interactiveClasses?: {\n hover?: string;\n transition?: string;\n };\n}\n\n// =============================================================================\n// FIELD VALUE RENDERER\n// =============================================================================\n\nexport interface FieldValueRendererProps {\n fieldName: string;\n value: unknown;\n fieldSchema: z.ZodTypeAny | undefined;\n fieldLabel: string | undefined;\n resourceType: string;\n tResourceFieldEnumValue: (resourceType: string, fieldName: string, enumValue: string) => string;\n}\n\n/**\n * Renders a single summary field VALUE through the shared decorator-driven\n * display pipeline (`DisplayField` → `DisplayValueDispatcher` → presets) — the\n * SAME path the detail/read view uses — so the FULL zod-UI display policy is\n * honored in lists/tables by default: enum → badge (+ per-value colors),\n * boolean → checkmark / yes-no, string → truncate / multiline, FK → reference,\n * date → formatted, money/bigdecimal/duration/etc.\n *\n * Previously this re-implemented a weak subset (enum-as-plain-text, naive array\n * join, `String(value)` fallback) that silently ignored every\n * `{primitive}UI().display` decorator, so listings/tables diverged from the\n * detail view (e.g. a `displayMode: BADGE` enum rendered as plain text). Routing\n * through `DisplayField` removes that divergence and the maintenance burden of a\n * parallel renderer.\n *\n * The card (\"Label:\" prefix) and the table (column header) own the label, so the\n * value is rendered under COMPACT chrome — a bare value with no framework\n * label/description wrapper. i18n enum labels are threaded explicitly via\n * `enumLabels` because there is no surrounding `DisplayTemplateContext`\n * (which is what supplies them inside `AutomaticDisplayer`).\n */\nexport function FieldValueRenderer({\n fieldName,\n value,\n fieldSchema,\n fieldLabel,\n resourceType,\n tResourceFieldEnumValue,\n}: FieldValueRendererProps): React.JSX.Element | null {\n // Build the enum value→label map from the same i18n translator the list\n // already uses, so enum badges show translated labels rather than raw enum\n // values. Hook runs unconditionally, before the null-value early return.\n const enumLabels = useMemo<Record<string, string> | undefined>(() => {\n if (!fieldSchema || getSchemaType(fieldSchema) !== FieldSchemaType.ENUM) return undefined;\n const enumValues = getEnumValues(fieldSchema);\n if (enumValues.length === 0) return undefined;\n return Object.fromEntries(\n enumValues.map((enumValue) => [enumValue, tResourceFieldEnumValue(resourceType, fieldName, enumValue)]),\n );\n }, [fieldSchema, resourceType, fieldName, tResourceFieldEnumValue]);\n\n if (value === null || value === undefined) return null;\n\n return (\n <div className=\"flex min-w-0 items-baseline gap-element-sm\">\n {fieldLabel && <span className=\"text-xs text-muted-foreground\">{fieldLabel}:</span>}\n {fieldSchema ? (\n // COMPACT chrome → bare value with the full decorator policy applied.\n <DisplayChromeProvider displayChromeMode={DisplayChromeMode.COMPACT}>\n <DisplayField\n name={fieldName}\n schema={fieldSchema}\n value={value}\n enumLabels={enumLabels}\n disableInlineEdit\n className=\"min-w-0 truncate text-sm text-muted-foreground\"\n />\n </DisplayChromeProvider>\n ) : (\n // No schema resolved for this field — `DisplayField` requires a schema,\n // so fall back to a plain string (matches the previous default branch).\n <span className=\"min-w-0 truncate text-sm text-muted-foreground\">{String(value)}</span>\n )}\n </div>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\nexport function ResourceListItemRenderer({\n item,\n resourceType,\n resourceConfig,\n registry,\n summaryFields,\n fieldLabelsMap,\n tResourceFieldEnumValue,\n onItemClick,\n operationsSlot,\n cardActionPlacement,\n selectable = false,\n isSelected = false,\n onToggleSelect,\n selectionActive = false,\n selectAriaLabel,\n className,\n interactiveClasses,\n}: ResourceListItemRendererProps): React.JSX.Element {\n if (!resourceConfig?.resourceFieldIdentifier) {\n throw FrontendResourceUtils.buildConfigurationError(\n ErrorCustomMessageReference.FRONTEND_RESOURCE_FIELD_IDENTIFIER_NOT_FOUND,\n { resourceType, component: 'ResourceListItemRenderer' }\n );\n }\n const fieldIdentifier = resourceConfig.resourceFieldIdentifier as string;\n // Resolve the record's own id (`_id`), not the cross-resource field-identifier\n // name (`<resource>Id`) which is absent from a plain record — see\n // FrontendResourceUtils.getResourceObjectId.\n const itemId = FrontendResourceUtils.getResourceObjectId(item, fieldIdentifier);\n\n // Primary display: use computeFrontendResourceLabel for the primary value.\n // For a junction this resolves the server-projected instance label (e.g. a\n // `forwardedLabel` virtual field referenced by `getFrontendResourceLabel`).\n const primaryLabel = useMemo((): string => {\n const label = FrontendResourceUtils.computeFrontendResourceLabel({\n resourceType,\n registry,\n currentObject: item,\n });\n return String(label || item[summaryFields[0]] || `${resourceType} #${itemId}`);\n }, [item, resourceType, registry, summaryFields, itemId]);\n\n // Secondary fields: every summary field EXCEPT the one already shown as the\n // primary label (title). The title comes from `computeFrontendResourceLabel`\n // — an arbitrary label strategy (a `string[]` join or a function), NOT\n // necessarily `summaryFields[0]`. Excluding the title positionally (`slice(1)`)\n // had two bugs: (a) it REPEATED the title when the label field wasn't first\n // (e.g. a list whose label is `name` but whose first summary field is `status`\n // → \"Name: …\" rendered under the identical title), and (b) it silently DROPPED\n // `summaryFields[0]` whenever that field wasn't the label. Excluding the title\n // by VALUE fixes both: the field whose value equals the title is the label\n // field wherever it sits, and every genuine non-label summary field is kept.\n // (The primary-scope FK is dropped upstream by\n // `ResourceLayoutUtils.detectSummaryFields`, so it never reaches here.)\n const secondaryFields = useMemo(\n () => summaryFields.filter((fieldName) => {\n const value = item[fieldName];\n if (value === null || value === undefined) return false;\n return String(value) !== primaryLabel;\n }),\n [summaryFields, item, primaryLabel],\n );\n\n // Schema shape for field type detection\n const schemaShape = useMemo(() => {\n const mainSchema = resourceConfig?.mainSchema;\n if (!mainSchema) return null;\n // Polymorphic resources feed a discriminated-union schema; resolve the union-aware field shape (else getObjectShape returns null for the union → no field labels).\n return AutomatedLayoutFrontendUtils.getObjectLikeFieldShape(mainSchema, { commonOnly: false }) ?? null;\n }, [resourceConfig]);\n\n // AUTHORED summary template — the `layout.summary` contract promises coverage\n // of \"cards, list items, FK references\". When a resource authors one, the card\n // defers its ENTIRE content region (title + body) to `ResourceSummaryDisplayer`:\n // the title is now a first-class template element (`<SummaryTitle />`, placed by\n // `<SummaryLayout>`), so a status badge can sit inline with the title and the\n // exact same composition renders in the FK popover. We pass the primary label we\n // already computed (correct `t`, junction forwardedLabel) into the displayer's\n // SummaryContext instead of rendering our own title above it.\n //\n // Resources WITHOUT an authored template keep the legacy host-owned title +\n // generic \"Label: value\" body: routing them through the GENERATED default\n // template would restyle every list in every app for no authored reason.\n const authoredSummaryTemplate = resourceConfig?.frontendConfiguration?.layout?.summary;\n\n const hoverClass = interactiveClasses?.hover ?? '';\n const transitionClass = interactiveClasses?.transition ?? '';\n // Resolve where the per-item actions sit (2-axis anchor → flex classes). The\n // content + actions share one flex container so actions never overlap text.\n const placement = resolveCardActionPlacementClasses(cardActionPlacement);\n\n // Mobile multi-select: a long-press ENTERS selection mode (selects this item);\n // while selection mode is active (`selectionActive`), a plain TAP toggles\n // selection instead of navigating. Gated to a mobile viewport — on desktop a\n // click always navigates and the checkbox is the explicit toggle. Long-press\n // is touch-only (the hook binds touch handlers).\n // Optional UIContext read: a rendering leaf shouldn't hard-require the shell's\n // UIProvider just to check the breakpoint — outside a provider (isolated\n // renders/tests) it degrades to desktop behavior (isMobile = false).\n const ui = React.useContext(UIContext);\n const isMobile = ui?.UIConfig?.currentBreakpoint === ViewportBreakpoint.MOBILE;\n const tapTogglesSelection = selectable && selectionActive && isMobile;\n // Long-press ENTERS selection mode — gated to the SAME condition as tap-toggle\n // (selectable + mobile viewport) so the two can't disagree (e.g. enter mode on\n // a tablet where tap-toggle is off, stranding the user mid-multiselect).\n const longPress = useLongPress(\n () => onToggleSelect?.(item),\n { enabled: selectable && !!onToggleSelect && isMobile },\n );\n\n const handleActivate = () => {\n // Swallow the synthetic click that follows a long-press (it already selected).\n if (longPress.consumeLongPress()) return;\n if (tapTogglesSelection) {\n onToggleSelect?.(item);\n return;\n }\n onItemClick(item);\n };\n\n return (\n <div\n className={cn(\n 'p-card', hoverClass, 'cursor-pointer', transitionClass,\n isSelected && 'bg-primary/5',\n // Prevent the browser text-selection that a long-press would start.\n selectable && 'select-none',\n className,\n )}\n onClick={handleActivate}\n role=\"button\"\n tabIndex={0}\n aria-label={String(primaryLabel)}\n {...longPress.handlers}\n // Selection state is conveyed by the row's checkbox (aria-selected is not\n // valid on role=\"button\"); the bg tint is a purely visual reinforcement.\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (tapTogglesSelection) {\n onToggleSelect?.(item);\n } else {\n onItemClick(item);\n }\n }\n }}\n >\n {/* Leading selection rail (only when the view is selectable). The checkbox\n toggles selection and stops propagation so it never triggers the row's\n navigation — mirrors the table select-cell contract. */}\n <div className=\"flex items-start gap-element-sm\">\n {/* stopPropagation on click, keydown AND touchstart: the row is a\n role=\"button\" with Enter/Space navigation and a touch long-press, so\n without these guards toggling the checkbox (tap/keyboard) would also\n navigate, and a hold over it would arm the row's long-press. */}\n {selectable && (\n <div\n className=\"shrink-0 pt-0.5\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n <CheckboxRoot\n checked={isSelected}\n onCheckedChange={() => onToggleSelect?.(item)}\n aria-label={selectAriaLabel ?? String(primaryLabel)}\n />\n </div>\n )}\n <div className={cn(placement.containerClass, 'min-w-0 flex-1')}>\n <div className={cn('space-y-item-xs', placement.contentClass)}>\n {authoredSummaryTemplate && resourceConfig?.mainSchema ? (\n // Authored template owns title + body. The title is fed via\n // SummaryContext (primaryLabel) and rendered by `<SummaryTitle />`\n // inside the template, so we do NOT render our own title here. The\n // region stays click-through so the whole card navigates (same as the\n // legacy body); an author who embeds a genuinely interactive control\n // stops propagation within their own template.\n <ResourceSummaryDisplayer\n resourceType={resourceType}\n schema={resourceConfig.mainSchema}\n data={item}\n primaryLabel={primaryLabel}\n />\n ) : (\n <>\n <Text as=\"p\" className=\"font-medium text-foreground\" truncate>{primaryLabel}</Text>\n {secondaryFields.map((fieldName) => {\n const value = item[fieldName];\n if (value === null || value === undefined) return null;\n return (\n <div key={fieldName}>\n <FieldValueRenderer\n fieldName={fieldName}\n value={value}\n fieldSchema={schemaShape?.[fieldName] as z.ZodTypeAny | undefined}\n fieldLabel={fieldLabelsMap[fieldName]}\n resourceType={resourceType}\n tResourceFieldEnumValue={tResourceFieldEnumValue}\n />\n </div>\n );\n })}\n </>\n )}\n </div>\n {operationsSlot && (\n // stopPropagation on click, keydown AND touchstart: an action tap or\n // keyboard activation must not also navigate the row, and a hold over\n // an action must not arm the row's long-press selection. (Mirrors the\n // checkbox rail — all three axes.)\n <div\n className={placement.actionsClass}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {operationsSlot}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -70,6 +70,18 @@ export interface ResourceHeaderTitleProps {
|
|
|
70
70
|
* The record's name, rendered with the page-header typography the default
|
|
71
71
|
* (template-less) header uses — so authoring a header template is visually
|
|
72
72
|
* neutral for the title itself and only adds what the template places around it.
|
|
73
|
+
*
|
|
74
|
+
* ## It is an `h1`, and the level is not cosmetic
|
|
75
|
+
*
|
|
76
|
+
* This IS the page's title — the record's name on a record page, the collection's on a listing — and
|
|
77
|
+
* there is exactly one per surface, so it owns the top of the document outline. It was an `h2` until
|
|
78
|
+
* 2026-09-09, which left generated screens with NO level-one heading at all: the first accessibility
|
|
79
|
+
* audit ever run against a live stack reported `page-has-heading-one` on the collection listing,
|
|
80
|
+
* where this element was the only heading on the page (#533).
|
|
81
|
+
*
|
|
82
|
+
* The classes are unchanged, so nothing moves visually — `text-xl` still sets the size. A heading
|
|
83
|
+
* level is the document's structure, not its appearance, and the two are deliberately decoupled here
|
|
84
|
+
* exactly as the design system does elsewhere.
|
|
73
85
|
*/
|
|
74
86
|
export declare function ResourceHeaderTitle({ className }: ResourceHeaderTitleProps): React.JSX.Element | null;
|
|
75
87
|
/** @wildo_source:part:end saas.frontend.resource-header-title */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceHeaderTitle.d.ts","sourceRoot":"","sources":["../../../../../src/components/resources/skeleton/ResourceHeaderTitle.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAoC,MAAM,OAAO,CAAC;AAQzD,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAErB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/C;AAID,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,8EAA8E;AAC9E,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAExD;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,IAAI,0BAA0B,GAAG,IAAI,CAEpF;AAMD,iHAAiH;AACjH,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED
|
|
1
|
+
{"version":3,"file":"ResourceHeaderTitle.d.ts","sourceRoot":"","sources":["../../../../../src/components/resources/skeleton/ResourceHeaderTitle.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAoC,MAAM,OAAO,CAAC;AAQzD,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAErB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/C;AAID,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,8EAA8E;AAC9E,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAExD;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,IAAI,0BAA0B,GAAG,IAAI,CAEpF;AAMD,iHAAiH;AACjH,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,EAAE,wBAAwB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAoBrG;AACD,iEAAiE"}
|
|
@@ -50,6 +50,18 @@ export function useOptionalResourceHeaderContext() {
|
|
|
50
50
|
* The record's name, rendered with the page-header typography the default
|
|
51
51
|
* (template-less) header uses — so authoring a header template is visually
|
|
52
52
|
* neutral for the title itself and only adds what the template places around it.
|
|
53
|
+
*
|
|
54
|
+
* ## It is an `h1`, and the level is not cosmetic
|
|
55
|
+
*
|
|
56
|
+
* This IS the page's title — the record's name on a record page, the collection's on a listing — and
|
|
57
|
+
* there is exactly one per surface, so it owns the top of the document outline. It was an `h2` until
|
|
58
|
+
* 2026-09-09, which left generated screens with NO level-one heading at all: the first accessibility
|
|
59
|
+
* audit ever run against a live stack reported `page-has-heading-one` on the collection listing,
|
|
60
|
+
* where this element was the only heading on the page (#533).
|
|
61
|
+
*
|
|
62
|
+
* The classes are unchanged, so nothing moves visually — `text-xl` still sets the size. A heading
|
|
63
|
+
* level is the document's structure, not its appearance, and the two are deliberately decoupled here
|
|
64
|
+
* exactly as the design system does elsewhere.
|
|
53
65
|
*/
|
|
54
66
|
export function ResourceHeaderTitle({ className }) {
|
|
55
67
|
const header = useOptionalResourceHeaderContext();
|
|
@@ -67,6 +79,6 @@ export function ResourceHeaderTitle({ className }) {
|
|
|
67
79
|
// only the space it needs, which is what keeps an authored badge HUGGING the
|
|
68
80
|
// name rather than being flung to the far end of the row (the same
|
|
69
81
|
// composition lesson recorded on `SummaryTitle`).
|
|
70
|
-
_jsx("
|
|
82
|
+
_jsx("h1", { className: cn('min-w-0 truncate text-xl font-semibold text-foreground', className), children: title }));
|
|
71
83
|
}
|
|
72
84
|
//# sourceMappingURL=ResourceHeaderTitle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceHeaderTitle.js","sourceRoot":"","sources":["../../../../../src/components/resources/skeleton/ResourceHeaderTitle.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAc,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AA0BpD,MAAM,qBAAqB,GAAG,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOrF,8EAA8E;AAC9E,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAkC,CAAC;AACnG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC;IAC9C,OAAO,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAC3C,CAAC;AAYD
|
|
1
|
+
{"version":3,"file":"ResourceHeaderTitle.js","sourceRoot":"","sources":["../../../../../src/components/resources/skeleton/ResourceHeaderTitle.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAc,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AA0BpD,MAAM,qBAAqB,GAAG,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOrF,8EAA8E;AAC9E,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAkC,CAAC;AACnG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC;IAC9C,OAAO,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAC3C,CAAC;AAYD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAE,SAAS,EAA4B;IACzE,MAAM,MAAM,GAAG,gCAAgC,EAAE,CAAC;IAClD,0EAA0E;IAC1E,4EAA4E;IAC5E,gFAAgF;IAChF,IAAI,MAAM,EAAE,iBAAiB;QAAE,MAAM,CAAC,iBAAiB,CAAC,YAAY,GAAG,IAAI,CAAC;IAE5E,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,CAAC;IAC5B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO;IACL,4EAA4E;IAC5E,4EAA4E;IAC5E,6EAA6E;IAC7E,mEAAmE;IACnE,kDAAkD;IAClD,aAAI,SAAS,EAAE,EAAE,CAAC,wDAAwD,EAAE,SAAS,CAAC,YACnF,KAAK,GACH,CACN,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\n/**\n * ResourceHeaderTitle — the record's page TITLE as a first-class, composable\n * element of an authored `layout.header` template.\n *\n * ## Why the header needs a template flow at all\n *\n * The READ page header owns a fixed band: back button, breadcrumb, resource\n * icon, the record's name, and the operation action cluster. Everything a\n * resource wanted to show ABOUT the record (a status badge, a visibility\n * marker, an accent colour) could previously only render in the `layout.display`\n * body BELOW that band — so the identity line said the record's name and nothing\n * else, while \"is this archived? is it public?\" sat in a separate labelled strip\n * underneath.\n *\n * `layout.header` closes that by letting the resource compose the middle of the\n * band itself. It is the same shape the SUMMARY surface already uses\n * (`SummaryLayout` / `SummaryTitle`), for the same reason recorded there: a\n * host-owned title plus a bolt-on accessory slot cannot express \"badge AFTER the\n * name\" or \"swatch BEFORE it\", whereas a template that places the title element\n * itself can express any of them with no extra config axis.\n *\n * ## Contract\n *\n * The header cedes the middle region to the template the moment `layout.header`\n * exists, so a template that never mounts `<ResourceHeaderTitle />` renders a\n * page with NO record name. That is loud in development (see the diagnostic in\n * `ResourceLayout_Header`) rather than silent.\n *\n * Renders nothing when the host resolved no label (a surface that shows a\n * generic operation title rather than one record's name) — the presence of a\n * label is the single data-driven signal that there is a title to place.\n */\n\nimport React, { createContext, useContext } from 'react';\n\nimport { cn } from '~wildo-frontend-lib/core/ui/cn';\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\nexport interface ResourceHeaderContextValue {\n /** Resource type key of the record whose header is rendering. */\n resourceType: string;\n\n /**\n * Resolved record title (the `getFrontendResourceLabel` result, or the\n * operation/resource title fallback), or `null` when this surface has no\n * record-specific name to show — `<ResourceHeaderTitle />` then renders nothing.\n */\n title: string | null;\n\n /**\n * Dev-diagnostic seam: `<ResourceHeaderTitle />` flips `titleMounted` when it\n * renders, letting the header detect an authored template that never places\n * the title. Mutable-ref shape (not state) on purpose — the flag is read only\n * after render and must never schedule a re-render.\n */\n titleMountTracker?: { titleMounted: boolean };\n}\n\nconst ResourceHeaderContext = createContext<ResourceHeaderContextValue | null>(null);\n\nexport interface ResourceHeaderContextProviderProps {\n value: ResourceHeaderContextValue;\n children: React.ReactNode;\n}\n\n/** Provides the resolved record title to an authored header template tree. */\nexport function ResourceHeaderContextProvider({\n value,\n children,\n}: ResourceHeaderContextProviderProps): React.JSX.Element {\n return <ResourceHeaderContext.Provider value={value}>{children}</ResourceHeaderContext.Provider>;\n}\n\n/**\n * Read the header context, or `null` when a header template is rendered outside\n * `ResourceLayout_Header` (e.g. previewed in isolation).\n */\nexport function useOptionalResourceHeaderContext(): ResourceHeaderContextValue | null {\n return useContext(ResourceHeaderContext);\n}\n\n// =============================================================================\n// TITLE ELEMENT\n// =============================================================================\n\n/** @wildo_source:part:start saas.frontend.resource-header-title facet:layer:frontend facet:family:resource-ui */\nexport interface ResourceHeaderTitleProps {\n /** Extra classes merged over the default page-title styling. */\n className?: string;\n}\n\n/**\n * The record's name, rendered with the page-header typography the default\n * (template-less) header uses — so authoring a header template is visually\n * neutral for the title itself and only adds what the template places around it.\n *\n * ## It is an `h1`, and the level is not cosmetic\n *\n * This IS the page's title — the record's name on a record page, the collection's on a listing — and\n * there is exactly one per surface, so it owns the top of the document outline. It was an `h2` until\n * 2026-09-09, which left generated screens with NO level-one heading at all: the first accessibility\n * audit ever run against a live stack reported `page-has-heading-one` on the collection listing,\n * where this element was the only heading on the page (#533).\n *\n * The classes are unchanged, so nothing moves visually — `text-xl` still sets the size. A heading\n * level is the document's structure, not its appearance, and the two are deliberately decoupled here\n * exactly as the design system does elsewhere.\n */\nexport function ResourceHeaderTitle({ className }: ResourceHeaderTitleProps): React.JSX.Element | null {\n const header = useOptionalResourceHeaderContext();\n // Register with the missing-title diagnostic BEFORE the empty-title early\n // return: mounting the element discharges the template's contract even when\n // this particular record resolves no label (data-driven, not an authoring bug).\n if (header?.titleMountTracker) header.titleMountTracker.titleMounted = true;\n\n const title = header?.title;\n if (!title) return null;\n\n return (\n // `min-w-0` + truncate so a long record name shrinks instead of pushing the\n // template's trailing badges out of the band. No `flex-1`: the title claims\n // only the space it needs, which is what keeps an authored badge HUGGING the\n // name rather than being flung to the far end of the row (the same\n // composition lesson recorded on `SummaryTitle`).\n <h1 className={cn('min-w-0 truncate text-xl font-semibold text-foreground', className)}>\n {title}\n </h1>\n );\n}\n/** @wildo_source:part:end saas.frontend.resource-header-title */\n"]}
|
|
@@ -1,64 +1,121 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AppPage_ConnectedAccountsSettings —
|
|
2
|
+
* AppPage_ConnectedAccountsSettings — connecting a third-party account, at either owner scope.
|
|
3
3
|
*
|
|
4
4
|
* A bespoke `ENGINE_PANEL`, and the reason is the shape of the act rather than the shape of the data.
|
|
5
|
-
* The stored credentials ARE a resource
|
|
5
|
+
* The stored credentials ARE a resource and the generic surface beside this one lists, inspects and
|
|
6
6
|
* deletes them perfectly. What it cannot do is CONNECT: that is not a row somebody creates, it is a
|
|
7
7
|
* flow that leaves the application, obtains consent at a vendor, and returns with a grant nobody
|
|
8
|
-
* typed. A resource screen has no mechanism for an action of that shape
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* ##
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* namespace, the panel gates rendering on `isReady` so it never flashes raw key paths, and a thin
|
|
38
|
-
* `t(key)` reads each label. Copy is owned by the labelling pipeline (sibling `.labels.ts`) — no
|
|
39
|
-
* hardcoded strings, and no humanized provider ref either: a provider identifier is an open
|
|
40
|
-
* vocabulary with no authored label, so it is rendered verbatim rather than invented from its own
|
|
41
|
-
* characters.
|
|
42
|
-
*
|
|
43
|
-
* assurance-control: WILDO.IDENTITY.DELEGATED_AUTHORIZATION — the surface on which a person
|
|
8
|
+
* typed. A resource screen has no mechanism for an action of that shape.
|
|
9
|
+
*
|
|
10
|
+
* ## Nothing is typed by hand
|
|
11
|
+
*
|
|
12
|
+
* The provider comes from a SELECT over what the application declares; the credential fields come
|
|
13
|
+
* from that provider's own secrets contract. Both were free text until this panel existed, which is
|
|
14
|
+
* how somebody ends up typing `secretSlug` into a box and storing a grant in a slot nothing reads.
|
|
15
|
+
*
|
|
16
|
+
* ## The flow follows the AUTH METHOD
|
|
17
|
+
*
|
|
18
|
+
* A connect surface cannot be one form. A vendor with an authorization flow gets the consent round
|
|
19
|
+
* trip; a vendor whose credential is typed gets a form over the fields IT declares. One button for
|
|
20
|
+
* both would be wrong for whichever it was not built around.
|
|
21
|
+
*
|
|
22
|
+
* ## A provider this deployment cannot connect is SHOWN, never hidden
|
|
23
|
+
*
|
|
24
|
+
* When the application has not registered with a vendor, the panel names the environment variables an
|
|
25
|
+
* operator must set. Removing the provider would answer "we do not support Asana", which is false and
|
|
26
|
+
* unfixable; naming `ASANA_CLIENT_ID` is true and is the thing that gets it set.
|
|
27
|
+
*
|
|
28
|
+
* ## One component, both scopes
|
|
29
|
+
*
|
|
30
|
+
* `ownerScope` decides whose account is being connected — a person's own, or the tenant's. The panel
|
|
31
|
+
* is otherwise identical because the ACT is identical, and duplicating it would guarantee the two
|
|
32
|
+
* drift. What must never be shared is the DATA: a person's connection and a tenant's shared
|
|
33
|
+
* integration have different owners, different authorization and different readers, and the scope is
|
|
34
|
+
* what keeps them apart at every call this panel makes.
|
|
35
|
+
*
|
|
36
|
+
* assurance-control: WILDO.IDENTITY.DELEGATED_AUTHORIZATION — the surface on which a principal
|
|
44
37
|
* authorizes this application to act toward a named third party on their behalf.
|
|
45
38
|
*/
|
|
46
39
|
import React from 'react';
|
|
40
|
+
import { ExternalProvider_Connection_DelegationMode, ExternalProvider_Connection_OwnerScope } from '@wildo-ai/external-connectors-models';
|
|
47
41
|
import { type ConnectableProviderView } from '../../core/requests/wildo-http-client-manual-calls.frontend.service';
|
|
48
42
|
/**
|
|
49
|
-
* The providers
|
|
43
|
+
* The providers connectable AT THIS SCOPE.
|
|
44
|
+
*
|
|
45
|
+
* Exported and named because it is the panel's one substantive claim: a provider admitting only
|
|
46
|
+
* ORGANIZATION connections must never be offered on a person's screen, and vice versa — offering it
|
|
47
|
+
* renders an action that answers 403 at the start leg.
|
|
48
|
+
*
|
|
49
|
+
* It deliberately does NOT filter on `connectable`. A provider this deployment cannot connect is
|
|
50
|
+
* still offered, because the panel's answer for it is the missing configuration rather than silence.
|
|
51
|
+
*/
|
|
52
|
+
export declare const selectProvidersForScope: (providers: readonly ConnectableProviderView[], ownerScope: ExternalProvider_Connection_OwnerScope) => ConnectableProviderView[];
|
|
53
|
+
/**
|
|
54
|
+
* ON WHOSE BEHALF this provider admits a connection at this scope — DERIVED, never assumed.
|
|
55
|
+
*
|
|
56
|
+
* The panel used to send `delegation: SERVICE` for every connect, at both scopes. That is one of the
|
|
57
|
+
* five declared shapes, so it worked by coincidence for the providers that happen to declare it and
|
|
58
|
+
* answered `409 CONNECTION_NOT_ADMISSIBLE` for the rest — the start leg checks the exact
|
|
59
|
+
* (ownerScope, delegation) PAIR against what the provider admits, and a scope does not imply a
|
|
60
|
+
* delegation.
|
|
61
|
+
*
|
|
62
|
+
* Measured across the generated catalogue on 2026-09-10, which is why this is a derivation rather
|
|
63
|
+
* than a better constant: at ORGANIZATION scope, 75 vendors declare `USER` and 39 declare `SERVICE`.
|
|
64
|
+
* A hard-coded `SERVICE` was therefore wrong for the larger half — `asana` among them, which is how
|
|
65
|
+
* this surfaced: the provider was listed, the button rendered, and the vendor was never reached.
|
|
66
|
+
*
|
|
67
|
+
* The pair is the provider's own statement, so the panel reads it rather than the person supplying
|
|
68
|
+
* it. Where a provider declares BOTH delegations for one scope — 27 vendors do, `box`, `dropbox`,
|
|
69
|
+
* `gmail` and `google-calendar` among them — the declaration genuinely does not decide, and only
|
|
70
|
+
* then is there a question to put to anybody. See `.claude/rules/derive-before-you-ask.md`.
|
|
71
|
+
*/
|
|
72
|
+
export declare const admissibleDelegationsForScope: (provider: ConnectableProviderView, ownerScope: ExternalProvider_Connection_OwnerScope) => ExternalProvider_Connection_DelegationMode[];
|
|
73
|
+
/**
|
|
74
|
+
* The target the start leg is given — assembled here so the shape is TESTABLE, not inlined.
|
|
75
|
+
*
|
|
76
|
+
* `ExternalProvider_ConnectionTargetSchema` refines two fields the panel cannot leave to be
|
|
77
|
+
* inferred, and it refuses in BOTH directions:
|
|
78
|
+
*
|
|
79
|
+
* - a `USER`-delegated call MUST name `actingForUserId` — "a delegated call acts FOR someone, and
|
|
80
|
+
* the user it acts for is what makes it permissible";
|
|
81
|
+
* - a `SERVICE` call MUST NOT, because naming a person there "reads as delegation the runtime will
|
|
82
|
+
* not perform, which is worse than an error: the call succeeds as the wrong identity".
|
|
50
83
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
84
|
+
* The panel sent neither until 2026-09-10, so every `USER`-delegated connect answered
|
|
85
|
+
* `400 VALIDATION_FAILED` — and that refusal names no field, so the screen could only report a
|
|
86
|
+
* generic failure. It was invisible because the shape that WORKS is `SERVICE`-delegated: `USER` at
|
|
87
|
+
* ORGANIZATION scope is what 75 of the harvested vendors declare, `asana` among them, and it is
|
|
88
|
+
* exactly the half nobody had driven.
|
|
53
89
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* have nowhere to navigate.
|
|
90
|
+
* `actingForUserId` is the SIGNED-IN person and is independent of `ownerId`: a tenant-owned
|
|
91
|
+
* connection acting for one of its members carries the organization in `ownerId` and the person
|
|
92
|
+
* here. At USER scope the two coincide, which is why one field cannot serve both.
|
|
58
93
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
94
|
+
* `ownerId` is omitted for APPLICATION, which the schema likewise refuses — "an application-owned
|
|
95
|
+
* connection has exactly one instance, so an ownerId here names something that does not exist".
|
|
96
|
+
* The Settings Hub renders only USER and ORGANIZATION today, so that arm is unreachable from this
|
|
97
|
+
* panel; it is handled because the function is the shape's one statement, and a caller that grows
|
|
98
|
+
* the third scope should not have to rediscover the rule.
|
|
61
99
|
*/
|
|
62
|
-
export declare const
|
|
63
|
-
|
|
100
|
+
export declare const buildConnectionTarget: (input: {
|
|
101
|
+
ownerScope: ExternalProvider_Connection_OwnerScope;
|
|
102
|
+
ownerId: string | null;
|
|
103
|
+
delegation: ExternalProvider_Connection_DelegationMode;
|
|
104
|
+
signedInUserId: string | null;
|
|
105
|
+
}) => {
|
|
106
|
+
ownerScope: ExternalProvider_Connection_OwnerScope;
|
|
107
|
+
delegation: ExternalProvider_Connection_DelegationMode;
|
|
108
|
+
ownerId?: string;
|
|
109
|
+
actingForUserId?: string;
|
|
110
|
+
};
|
|
111
|
+
export interface ConnectedAccountsSettingsProps {
|
|
112
|
+
/**
|
|
113
|
+
* WHOSE account is being connected.
|
|
114
|
+
*
|
|
115
|
+
* A prop rather than two components: the act is identical and duplicating it would guarantee the
|
|
116
|
+
* two screens drift. The DATA never mixes — every call this panel makes carries the scope.
|
|
117
|
+
*/
|
|
118
|
+
ownerScope: ExternalProvider_Connection_OwnerScope;
|
|
119
|
+
}
|
|
120
|
+
export declare const AppPage_ConnectedAccountsSettings: React.FC<Partial<ConnectedAccountsSettingsProps>>;
|
|
64
121
|
//# sourceMappingURL=AppPage_ConnectedAccountsSettings.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppPage_ConnectedAccountsSettings.d.ts","sourceRoot":"","sources":["../../../../src/components/settings/AppPage_ConnectedAccountsSettings.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AppPage_ConnectedAccountsSettings.d.ts","sourceRoot":"","sources":["../../../../src/components/settings/AppPage_ConnectedAccountsSettings.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAIjF,OAAO,EACL,0CAA0C,EAC1C,sCAAsC,EACvC,MAAM,sCAAsC,CAAC;AAS9C,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,mFAAmF,CAAC;AAS3F;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAClC,WAAW,SAAS,uBAAuB,EAAE,EAC7C,YAAY,sCAAsC,KACjD,uBAAuB,EAExB,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,GACxC,UAAU,uBAAuB,EACjC,YAAY,sCAAsC,KACjD,0CAA0C,EAY5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO;IAC3C,UAAU,EAAE,sCAAsC,CAAC;IACnD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,0CAA0C,CAAC;IACvD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,KAAG;IACF,UAAU,EAAE,sCAAsC,CAAC;IACnD,UAAU,EAAE,0CAA0C,CAAC;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAUzB,CAAC;AA0CH,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,UAAU,EAAE,sCAAsC,CAAC;CACpD;AAED,eAAO,MAAM,iCAAiC,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAia/F,CAAC"}
|