@jini-ai/admin 0.4.2 → 0.4.3
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/CHANGELOG.md +11 -0
- package/README.md +7 -0
- package/dist/agent-plugins/react/components/AgentPluginListPanel.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +1 -1
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/index.js +1 -1
- package/dist/agent-plugins/react/pages/AgentPluginsPage.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/DownloadedTab.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/InstalledTab.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/MarketplaceTab.d.ts +1 -1
- package/dist/comments/adapters/http.d.ts +29 -0
- package/dist/comments/adapters/http.js +50 -0
- package/dist/comments/adapters/memory.d.ts +17 -0
- package/dist/comments/adapters/memory.js +61 -0
- package/dist/comments/comments.module.d.ts +73 -0
- package/dist/comments/comments.module.js +16 -0
- package/dist/comments/conformance/comments-api.conformance.d.ts +9 -0
- package/dist/comments/conformance/comments-api.conformance.js +63 -0
- package/dist/comments/controllers/comment-queue.controller.d.ts +48 -0
- package/dist/comments/controllers/comment-queue.controller.js +164 -0
- package/dist/comments/controllers/comment-settings.controller.d.ts +32 -0
- package/dist/comments/controllers/comment-settings.controller.js +77 -0
- package/dist/comments/controllers/comments.controller.d.ts +16 -0
- package/dist/comments/controllers/comments.controller.js +16 -0
- package/dist/comments/index.d.ts +13 -0
- package/dist/comments/index.js +8 -0
- package/dist/comments/messages.en.d.ts +54 -0
- package/dist/comments/messages.en.js +56 -0
- package/dist/comments/models.d.ts +22 -0
- package/dist/comments/models.js +2 -0
- package/dist/comments/ports.d.ts +14 -0
- package/dist/comments/ports.js +5 -0
- package/dist/comments/react/components/QueueSection.d.ts +30 -0
- package/dist/comments/react/components/QueueSection.js +57 -0
- package/dist/comments/react/components/SettingsSection.d.ts +14 -0
- package/dist/comments/react/components/SettingsSection.js +23 -0
- package/dist/comments/react/hooks/CommentsPage.hooks.d.ts +9 -0
- package/dist/comments/react/hooks/CommentsPage.hooks.js +10 -0
- package/dist/comments/react/hooks/CommentsPorts.hooks.d.ts +13 -0
- package/dist/comments/react/hooks/CommentsPorts.hooks.js +15 -0
- package/dist/comments/react/hooks/QueueSection.hooks.d.ts +62 -0
- package/dist/comments/react/hooks/QueueSection.hooks.js +57 -0
- package/dist/comments/react/hooks/SettingsSection.hooks.d.ts +12 -0
- package/dist/comments/react/hooks/SettingsSection.hooks.js +21 -0
- package/dist/comments/react/hooks/use-comment-queue.hooks.d.ts +34 -0
- package/dist/comments/react/hooks/use-comment-queue.hooks.js +47 -0
- package/dist/comments/react/hooks/use-comment-settings.hooks.d.ts +22 -0
- package/dist/comments/react/hooks/use-comment-settings.hooks.js +30 -0
- package/dist/comments/react/hooks/use-comments.hooks.d.ts +15 -0
- package/dist/comments/react/hooks/use-comments.hooks.js +20 -0
- package/dist/comments/react/index.d.ts +316 -0
- package/dist/comments/react/index.js +35 -0
- package/dist/comments/react/pages/CommentsPage.d.ts +12 -0
- package/dist/comments/react/pages/CommentsPage.js +15 -0
- package/dist/comments/rules.d.ts +140 -0
- package/dist/comments/rules.js +199 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +1 -0
- package/dist/core/ledger-slice.d.ts +90 -0
- package/dist/core/ledger-slice.js +81 -0
- package/dist/core/ports/comments.d.ts +2 -0
- package/dist/core/ports/forms.d.ts +19 -7
- package/dist/core/ports/forms.js +4 -4
- package/dist/core/ports/index.d.ts +1 -1
- package/dist/core/ports/seo.d.ts +33 -6
- package/dist/core/react/bind-react.d.ts +46 -0
- package/dist/core/react/bind-react.js +28 -0
- package/dist/core/react/index.d.ts +3 -0
- package/dist/core/react/index.js +3 -0
- package/dist/core/react/use-controller.d.ts +16 -0
- package/dist/core/react/use-controller.js +18 -0
- package/dist/forms/adapters/http.d.ts +26 -0
- package/dist/forms/adapters/http.js +43 -0
- package/dist/forms/adapters/memory.d.ts +14 -0
- package/dist/forms/adapters/memory.js +77 -0
- package/dist/forms/conformance/forms-api.conformance.d.ts +17 -0
- package/dist/forms/conformance/forms-api.conformance.js +71 -0
- package/dist/forms/controllers/draft-store.d.ts +18 -0
- package/dist/forms/controllers/draft-store.js +17 -0
- package/dist/forms/controllers/field-attributes-dialog.controller.d.ts +37 -0
- package/dist/forms/controllers/field-attributes-dialog.controller.js +27 -0
- package/dist/forms/controllers/form-editor.controller.d.ts +36 -0
- package/dist/forms/controllers/form-editor.controller.js +22 -0
- package/dist/forms/controllers/form-fields-editor.controller.d.ts +20 -0
- package/dist/forms/controllers/form-fields-editor.controller.js +10 -0
- package/dist/forms/controllers/form-submission-detail.controller.d.ts +20 -0
- package/dist/forms/controllers/form-submission-detail.controller.js +10 -0
- package/dist/forms/controllers/form-submissions.controller.d.ts +25 -0
- package/dist/forms/controllers/form-submissions.controller.js +10 -0
- package/dist/forms/controllers/forms-list.controller.d.ts +22 -0
- package/dist/forms/controllers/forms-list.controller.js +10 -0
- package/dist/forms/forms.module.d.ts +137 -0
- package/dist/forms/forms.module.js +17 -0
- package/dist/forms/html-rules.d.ts +20 -0
- package/dist/forms/html-rules.js +24 -0
- package/dist/forms/index.d.ts +14 -0
- package/dist/forms/index.js +12 -0
- package/dist/forms/messages.en.d.ts +100 -0
- package/dist/forms/messages.en.js +104 -0
- package/dist/forms/models.d.ts +29 -0
- package/dist/forms/models.js +8 -0
- package/dist/forms/ports.d.ts +26 -0
- package/dist/forms/ports.js +6 -0
- package/dist/forms/react/components/FieldAttributesDialog.d.ts +14 -0
- package/dist/forms/react/components/FieldAttributesDialog.js +47 -0
- package/dist/forms/react/components/FormEditorFieldsBody.d.ts +28 -0
- package/dist/forms/react/components/FormEditorFieldsBody.js +24 -0
- package/dist/forms/react/components/FormEditorHeaderText.d.ts +7 -0
- package/dist/forms/react/components/FormEditorHeaderText.js +8 -0
- package/dist/forms/react/components/FormEditorMainPanel.d.ts +12 -0
- package/dist/forms/react/components/FormEditorMainPanel.js +13 -0
- package/dist/forms/react/components/FormEditorTabStrip.d.ts +13 -0
- package/dist/forms/react/components/FormEditorTabStrip.js +14 -0
- package/dist/forms/react/components/FormFieldsEditor.d.ts +13 -0
- package/dist/forms/react/components/FormFieldsEditor.js +39 -0
- package/dist/forms/react/components/FormSubmissionDate.d.ts +8 -0
- package/dist/forms/react/components/FormSubmissionDate.js +5 -0
- package/dist/forms/react/components/FormSubmissionDetail.d.ts +13 -0
- package/dist/forms/react/components/FormSubmissionDetail.js +29 -0
- package/dist/forms/react/components/FormSubmissions.d.ts +12 -0
- package/dist/forms/react/components/FormSubmissions.js +46 -0
- package/dist/forms/react/hooks/FormsPorts.hooks.d.ts +41 -0
- package/dist/forms/react/hooks/FormsPorts.hooks.js +16 -0
- package/dist/forms/react/hooks/forms-view.hooks.d.ts +122 -0
- package/dist/forms/react/hooks/forms-view.hooks.js +79 -0
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.d.ts +31 -0
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.js +22 -0
- package/dist/forms/react/hooks/use-form-editor.hooks.d.ts +118 -0
- package/dist/forms/react/hooks/use-form-editor.hooks.js +208 -0
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.d.ts +34 -0
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.js +36 -0
- package/dist/forms/react/hooks/use-form-submission-date.hooks.d.ts +12 -0
- package/dist/forms/react/hooks/use-form-submission-date.hooks.js +9 -0
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.d.ts +68 -0
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.js +81 -0
- package/dist/forms/react/hooks/use-form-submissions.hooks.d.ts +67 -0
- package/dist/forms/react/hooks/use-form-submissions.hooks.js +110 -0
- package/dist/forms/react/hooks/use-forms-list.hooks.d.ts +78 -0
- package/dist/forms/react/hooks/use-forms-list.hooks.js +103 -0
- package/dist/forms/react/index.d.ts +449 -0
- package/dist/forms/react/index.js +35 -0
- package/dist/forms/react/pages/FormEditor.d.ts +19 -0
- package/dist/forms/react/pages/FormEditor.js +40 -0
- package/dist/forms/react/pages/FormsList.d.ts +39 -0
- package/dist/forms/react/pages/FormsList.js +55 -0
- package/dist/forms/rules.d.ts +291 -0
- package/dist/forms/rules.js +338 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +1 -1
- package/dist/media/react/hooks/LibraryTab.hooks.d.ts +1 -1
- package/dist/media/react/hooks/LibraryTab.hooks.js +1 -1
- package/dist/media/react/hooks/MediaCard.hooks.js +6 -20
- package/dist/media/react/hooks/MediaGrid.hooks.d.ts +2 -2
- package/dist/media/react/hooks/MediaPage.hooks.d.ts +1 -1
- package/dist/media/react/hooks/MediaPage.hooks.js +1 -1
- package/dist/media/react/hooks/MediaPicker.hooks.js +1 -1
- package/dist/media/react/index.js +1 -1
- package/dist/media/react/tabs/LibraryTab.d.ts +1 -1
- package/dist/media/react/tabs/ProvidersTab.d.ts +1 -1
- package/dist/playground/react/components/Canvas.d.ts +1 -1
- package/dist/playground/react/hooks/Canvas.hooks.d.ts +1 -1
- package/dist/playground/react/hooks/Canvas.hooks.js +1 -1
- package/dist/playground/react/hooks/PlaygroundPage.hooks.d.ts +1 -1
- package/dist/playground/react/index.js +1 -1
- package/dist/playground/react/pages/PlaygroundPage.d.ts +1 -1
- package/dist/playground/react/tabs/CanvasTab.d.ts +1 -1
- package/dist/react/bind-react.d.ts +1 -45
- package/dist/react/bind-react.js +2 -27
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +22 -5
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +2 -2
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +17 -55
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js +33 -70
- package/dist/react/use-controller.d.ts +1 -15
- package/dist/react/use-controller.js +2 -17
- package/dist/redirects/adapters/http.d.ts +20 -0
- package/dist/redirects/adapters/http.js +31 -0
- package/dist/redirects/adapters/memory.d.ts +13 -0
- package/dist/redirects/adapters/memory.js +83 -0
- package/dist/redirects/conformance/redirects-api.conformance.d.ts +12 -0
- package/dist/redirects/conformance/redirects-api.conformance.js +66 -0
- package/dist/redirects/controllers/hit-count.controller.d.ts +14 -0
- package/dist/redirects/controllers/hit-count.controller.js +10 -0
- package/dist/redirects/controllers/import-redirects.controller.d.ts +28 -0
- package/dist/redirects/controllers/import-redirects.controller.js +29 -0
- package/dist/redirects/controllers/redirects.controller.d.ts +37 -0
- package/dist/redirects/controllers/redirects.controller.js +38 -0
- package/dist/redirects/index.d.ts +11 -0
- package/dist/redirects/index.js +10 -0
- package/dist/redirects/messages.en.d.ts +56 -0
- package/dist/redirects/messages.en.js +55 -0
- package/dist/redirects/models.d.ts +14 -0
- package/dist/redirects/models.js +2 -0
- package/dist/redirects/ports.d.ts +9 -0
- package/dist/redirects/ports.js +4 -0
- package/dist/redirects/react/components/HitCountCell.d.ts +19 -0
- package/dist/redirects/react/components/HitCountCell.js +19 -0
- package/dist/redirects/react/components/ImportRedirectsForm.d.ts +17 -0
- package/dist/redirects/react/components/ImportRedirectsForm.js +12 -0
- package/dist/redirects/react/hooks/RedirectsOptions.hooks.d.ts +27 -0
- package/dist/redirects/react/hooks/RedirectsOptions.hooks.js +15 -0
- package/dist/redirects/react/hooks/RedirectsPage.hooks.d.ts +50 -0
- package/dist/redirects/react/hooks/RedirectsPage.hooks.js +30 -0
- package/dist/redirects/react/hooks/RedirectsPorts.hooks.d.ts +10 -0
- package/dist/redirects/react/hooks/RedirectsPorts.hooks.js +11 -0
- package/dist/redirects/react/hooks/use-hit-count-cell.hooks.d.ts +19 -0
- package/dist/redirects/react/hooks/use-hit-count-cell.hooks.js +15 -0
- package/dist/redirects/react/hooks/use-import-redirects-form.hooks.d.ts +21 -0
- package/dist/redirects/react/hooks/use-import-redirects-form.hooks.js +25 -0
- package/dist/redirects/react/hooks/use-redirects.hooks.d.ts +34 -0
- package/dist/redirects/react/hooks/use-redirects.hooks.js +63 -0
- package/dist/redirects/react/hooks/wired.hooks.d.ts +55 -0
- package/dist/redirects/react/hooks/wired.hooks.js +31 -0
- package/dist/redirects/react/index.d.ts +316 -0
- package/dist/redirects/react/index.js +28 -0
- package/dist/redirects/react/pages/RedirectsPage.d.ts +15 -0
- package/dist/redirects/react/pages/RedirectsPage.js +20 -0
- package/dist/redirects/redirects.module.d.ts +75 -0
- package/dist/redirects/redirects.module.js +13 -0
- package/dist/redirects/rules.d.ts +131 -0
- package/dist/redirects/rules.js +134 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +1 -1
- package/dist/security/react/hooks/AccessTokensTab.hooks.js +1 -1
- package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +1 -1
- package/dist/security/react/hooks/RootKeyTab.hooks.js +1 -1
- package/dist/security/react/hooks/SecurityPage.hooks.d.ts +1 -1
- package/dist/security/react/index.js +1 -1
- package/dist/security/react/pages/SecurityPage.d.ts +1 -1
- package/dist/security/react/tabs/AccessTokensTab.d.ts +1 -1
- package/dist/security/react/tabs/RootKeyTab.d.ts +1 -1
- package/dist/seo/adapters/http.d.ts +31 -0
- package/dist/seo/adapters/http.js +37 -0
- package/dist/seo/adapters/memory.d.ts +28 -0
- package/dist/seo/adapters/memory.js +129 -0
- package/dist/seo/conformance/seo-api.conformance.d.ts +8 -0
- package/dist/seo/conformance/seo-api.conformance.js +44 -0
- package/dist/seo/controllers/entry-picker.controller.d.ts +21 -0
- package/dist/seo/controllers/entry-picker.controller.js +17 -0
- package/dist/seo/controllers/entry-section.controller.d.ts +20 -0
- package/dist/seo/controllers/entry-section.controller.js +7 -0
- package/dist/seo/controllers/media-ref.controller.d.ts +26 -0
- package/dist/seo/controllers/media-ref.controller.js +14 -0
- package/dist/seo/controllers/seo-entry.controller.d.ts +31 -0
- package/dist/seo/controllers/seo-entry.controller.js +52 -0
- package/dist/seo/controllers/seo.controller.d.ts +29 -0
- package/dist/seo/controllers/seo.controller.js +50 -0
- package/dist/seo/controllers/sitemap.controller.d.ts +25 -0
- package/dist/seo/controllers/sitemap.controller.js +27 -0
- package/dist/seo/index.d.ts +14 -0
- package/dist/seo/index.js +12 -0
- package/dist/seo/messages.en.d.ts +78 -0
- package/dist/seo/messages.en.js +78 -0
- package/dist/seo/models.d.ts +179 -0
- package/dist/seo/models.js +8 -0
- package/dist/seo/ports.d.ts +11 -0
- package/dist/seo/ports.js +4 -0
- package/dist/seo/react/components/MediaPickerBridge.d.ts +4 -0
- package/dist/seo/react/components/MediaPickerBridge.js +8 -0
- package/dist/seo/react/components/MediaRefField.d.ts +36 -0
- package/dist/seo/react/components/MediaRefField.js +12 -0
- package/dist/seo/react/components/SeoIcons.d.ts +13 -0
- package/dist/seo/react/components/SeoIcons.js +32 -0
- package/dist/seo/react/components/SitemapModal.d.ts +48 -0
- package/dist/seo/react/components/SitemapModal.js +70 -0
- package/dist/seo/react/hooks/MediaPickerBridge.hooks.d.ts +6 -0
- package/dist/seo/react/hooks/MediaPickerBridge.hooks.js +27 -0
- package/dist/seo/react/hooks/MediaRefField.hooks.d.ts +22 -0
- package/dist/seo/react/hooks/MediaRefField.hooks.js +30 -0
- package/dist/seo/react/hooks/SeoModuleTabs.d.ts +14 -0
- package/dist/seo/react/hooks/SeoModuleTabs.js +18 -0
- package/dist/seo/react/hooks/SeoPage.hooks.d.ts +69 -0
- package/dist/seo/react/hooks/SeoPage.hooks.js +37 -0
- package/dist/seo/react/hooks/SeoPorts.hooks.d.ts +19 -0
- package/dist/seo/react/hooks/SeoPorts.hooks.js +22 -0
- package/dist/seo/react/hooks/SitemapModal.hooks.d.ts +26 -0
- package/dist/seo/react/hooks/SitemapModal.hooks.js +20 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.d.ts +11 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.js +20 -0
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.d.ts +14 -0
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.js +26 -0
- package/dist/seo/react/hooks/use-seo-entry-section.hooks.d.ts +4 -0
- package/dist/seo/react/hooks/use-seo-entry-section.hooks.js +7 -0
- package/dist/seo/react/hooks/use-seo.hooks.d.ts +11 -0
- package/dist/seo/react/hooks/use-seo.hooks.js +29 -0
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.d.ts +10 -0
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.js +18 -0
- package/dist/seo/react/index.d.ts +366 -0
- package/dist/seo/react/index.js +30 -0
- package/dist/seo/react/options.d.ts +19 -0
- package/dist/seo/react/options.js +2 -0
- package/dist/seo/react/pages/SeoPage.d.ts +161 -0
- package/dist/seo/react/pages/SeoPage.js +215 -0
- package/dist/seo/rules.d.ts +136 -0
- package/dist/seo/rules.js +212 -0
- package/dist/seo/seo.module.d.ts +114 -0
- package/dist/seo/seo.module.js +14 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +1 -1
- package/dist/source-control/react/hooks/ProvidersTab.hooks.js +1 -1
- package/dist/source-control/react/hooks/SourceControlPage.hooks.d.ts +1 -1
- package/dist/source-control/react/index.js +1 -1
- package/dist/source-control/react/pages/SourceControlPage.d.ts +1 -1
- package/dist/source-control/react/tabs/ProvidersTab.d.ts +1 -1
- package/dist/widgets/adapters/http.d.ts +19 -0
- package/dist/widgets/adapters/http.js +33 -0
- package/dist/widgets/adapters/memory.d.ts +22 -0
- package/dist/widgets/adapters/memory.js +93 -0
- package/dist/widgets/conformance/widgets-api.conformance.d.ts +6 -0
- package/dist/widgets/conformance/widgets-api.conformance.js +55 -0
- package/dist/widgets/controllers/instance-editor.controller.d.ts +49 -0
- package/dist/widgets/controllers/instance-editor.controller.js +87 -0
- package/dist/widgets/controllers/library.controller.d.ts +33 -0
- package/dist/widgets/controllers/library.controller.js +53 -0
- package/dist/widgets/controllers/region-editor.controller.d.ts +42 -0
- package/dist/widgets/controllers/region-editor.controller.js +69 -0
- package/dist/widgets/controllers/regions.controller.d.ts +27 -0
- package/dist/widgets/controllers/regions.controller.js +44 -0
- package/dist/widgets/index.d.ts +11 -0
- package/dist/widgets/index.js +9 -0
- package/dist/widgets/messages.en.d.ts +78 -0
- package/dist/widgets/messages.en.js +83 -0
- package/dist/widgets/models.d.ts +62 -0
- package/dist/widgets/models.js +2 -0
- package/dist/widgets/ports.d.ts +79 -0
- package/dist/widgets/ports.js +5 -0
- package/dist/widgets/react/components/WidgetInstanceNotices.d.ts +14 -0
- package/dist/widgets/react/components/WidgetInstanceNotices.js +25 -0
- package/dist/widgets/react/components/WidgetRegionEditorHeaderActions.d.ts +22 -0
- package/dist/widgets/react/components/WidgetRegionEditorHeaderActions.js +18 -0
- package/dist/widgets/react/components/WidgetsLibraryNotices.d.ts +13 -0
- package/dist/widgets/react/components/WidgetsLibraryNotices.js +10 -0
- package/dist/widgets/react/hooks/WidgetsPorts.hooks.d.ts +270 -0
- package/dist/widgets/react/hooks/WidgetsPorts.hooks.js +17 -0
- package/dist/widgets/react/hooks/navigation.hooks.d.ts +9 -0
- package/dist/widgets/react/hooks/navigation.hooks.js +6 -0
- package/dist/widgets/react/hooks/page-views.hooks.d.ts +87 -0
- package/dist/widgets/react/hooks/page-views.hooks.js +30 -0
- package/dist/widgets/react/hooks/use-widget-instance-editor.hooks.d.ts +56 -0
- package/dist/widgets/react/hooks/use-widget-instance-editor.hooks.js +38 -0
- package/dist/widgets/react/hooks/use-widget-region-editor.hooks.d.ts +32 -0
- package/dist/widgets/react/hooks/use-widget-region-editor.hooks.js +22 -0
- package/dist/widgets/react/hooks/use-widget-regions.hooks.d.ts +23 -0
- package/dist/widgets/react/hooks/use-widget-regions.hooks.js +22 -0
- package/dist/widgets/react/hooks/use-widgets-library.hooks.d.ts +37 -0
- package/dist/widgets/react/hooks/use-widgets-library.hooks.js +23 -0
- package/dist/widgets/react/index.d.ts +391 -0
- package/dist/widgets/react/index.js +31 -0
- package/dist/widgets/react/pages/WidgetInstanceEditor.d.ts +23 -0
- package/dist/widgets/react/pages/WidgetInstanceEditor.js +37 -0
- package/dist/widgets/react/pages/WidgetRegionEditor.d.ts +22 -0
- package/dist/widgets/react/pages/WidgetRegionEditor.js +25 -0
- package/dist/widgets/react/pages/WidgetRegions.d.ts +21 -0
- package/dist/widgets/react/pages/WidgetRegions.js +31 -0
- package/dist/widgets/react/pages/WidgetsLibrary.d.ts +24 -0
- package/dist/widgets/react/pages/WidgetsLibrary.js +61 -0
- package/dist/widgets/rules.d.ts +94 -0
- package/dist/widgets/rules.js +83 -0
- package/dist/widgets/widgets.module.d.ts +122 -0
- package/dist/widgets/widgets.module.js +21 -0
- package/package.json +206 -9
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { AdminFormsPort } from "../../../core/ports/forms.js";
|
|
2
|
+
import { type FormAnswerColumn } from "../../html-rules.js";
|
|
3
|
+
import type { AdminFormDefinition, AdminFormField } from "../../models.js";
|
|
4
|
+
/**
|
|
5
|
+
* @file Everything the FormEditor SCREEN does — load, save, status toggle, and the Fields/
|
|
6
|
+
* Submissions tab strip's roving-tabindex focus management — so `FormEditor`'s exported component
|
|
7
|
+
* in `FormEditor.tsx` is only markup.
|
|
8
|
+
*
|
|
9
|
+
* Moved from the host — same load effect, same save/status-toggle error strings, same "later failure
|
|
10
|
+
* keeps the editor on screen" guard (the audit-blocker fix this screen's file header describes —
|
|
11
|
+
* see `FormEditor.tsx`'s own header for the full rationale, which stays with the guard in the view
|
|
12
|
+
* since it describes render behaviour, not this hook's state).
|
|
13
|
+
*
|
|
14
|
+
* `tab` (ADR-063, 2026-08-31): no longer local state — it is `props.tab`, derived from the route
|
|
15
|
+
* (`/forms/:formId` vs `/forms/:formId/submissions`) by `panels.tsx`/`FormEditor.tsx`. Switching
|
|
16
|
+
* tabs calls the injected `navigate` (real app router), the same idiom `Deployment.tsx`'s tab strip
|
|
17
|
+
* uses, rather than a local `setTab` — Submissions has its own independent fetch, so a tab switch
|
|
18
|
+
* is a genuine route change, not a display-layer filter over data already in hand.
|
|
19
|
+
*
|
|
20
|
+
* `tabRefs` moves here too, per Pattern 1 (every `useRef` moves with state/effects, not just
|
|
21
|
+
* `useState`) — the view still attaches each button via its own `ref` callback (an unavoidably
|
|
22
|
+
* DOM-side operation), but the ref array itself and the keydown-to-focus-change logic
|
|
23
|
+
* (`onTabsKeyDown`) are behaviour, not markup.
|
|
24
|
+
*
|
|
25
|
+
* Naming follows `hooks/use-settings-slice.hooks.ts`: `use-<thing>.hooks.ts`. Feature-local because
|
|
26
|
+
* nothing outside `features/forms` needs it.
|
|
27
|
+
*
|
|
28
|
+
* `port`/`navigate` are injected — see `core/ports/forms.ts` (shared with `use-forms-list.hooks.ts`,
|
|
29
|
+
* since both read/write the same `AdminFormDefinition` resource) — rather than reaching `lib/api`/
|
|
30
|
+
* `lib/router` directly, so a test can describe load/save outcomes against `the memory adapter`
|
|
31
|
+
* instead of stubbing global `fetch`. `useWiredFormEditor` below is the zero-argument pair
|
|
32
|
+
* `FormEditor.tsx` actually mounts.
|
|
33
|
+
*
|
|
34
|
+
* `t` (standing i18n rule — a component with a hook gets a BOUND `t` from that hook, not its own
|
|
35
|
+
* `useAdminLocale()`/dictionary import, same shape `use-post-editor.hooks.ts` established for this
|
|
36
|
+
* conversion): injected alongside `port`/`navigate` because `FormEditor.tsx` itself (the copy
|
|
37
|
+
* around this hook's own state — tab labels, Save button, etc.) DOES need translated strings, even
|
|
38
|
+
* though this hook's own error messages don't. Pre-bound to `(key: string) => string`.
|
|
39
|
+
* `useAdminLocale()` and `FORMS_DICT` are called/read only inside {@link useWiredFormEditor}.
|
|
40
|
+
*
|
|
41
|
+
* `@jini-ai/ui/fetch-query` migration (2026-08-12): the load is one `useFetchQuery` keyed on
|
|
42
|
+
* `KEYS.form(formId)` (disabled for `isNew`, matching the original `if (isNew) return;` early-out).
|
|
43
|
+
* `form`/`name`/`slug`/`fields`/`notify` live in the section controller — the operator edits `name`/`slug`/
|
|
44
|
+
* `fields`; `notify` has no UI of its own (owner ask 2026-09-22 removed the notify checkbox/
|
|
45
|
+
* recipients input — see this file's own `notify` state comment) and is only ever carried through
|
|
46
|
+
* unchanged. All are seeded from `list.data` exactly once per `formId` via `seededFormIdRef`, the
|
|
47
|
+
* same shape `collections/hooks/use-collection-entry-editor.hooks.ts`'s `seededIdentityRef`
|
|
48
|
+
* establishes (see that file's header for the regression it guards against: a background refetch of
|
|
49
|
+
* the SAME identity must not clobber in-progress edits). `handleSave`/`handleStatusToggle` set `form`
|
|
50
|
+
* (and, for save, the rest of the seeded fields) directly from each MUTATION's own response, and
|
|
51
|
+
* neither mutation invalidates this hook's OWN `KEYS.form(formId)` read (only the sibling
|
|
52
|
+
* `KEYS.list`) — mirroring `save()`/`toggleLifecycle()` in that same collections hook, which
|
|
53
|
+
* invalidate only the sibling `KEYS.entries(...)`, never their own `KEYS.entry(...)`, for the
|
|
54
|
+
* identical reason: a response already in hand needs no redundant background refetch of itself. This
|
|
55
|
+
* eliminates the load race an external audit flagged at this file's old line 117 (`useEffect(load,
|
|
56
|
+
* [props.formId, port])` with no cancellation guard, so a formId change mid-flight could commit a
|
|
57
|
+
* stale response): a keyed query cannot commit a response belonging to a prior key, by construction.
|
|
58
|
+
*/
|
|
59
|
+
export interface FormEditorController {
|
|
60
|
+
isNew: boolean;
|
|
61
|
+
/** `null` until the initial load settles (or always, for `isNew`) — the caller renders a loading
|
|
62
|
+
* state or the empty create form accordingly. */
|
|
63
|
+
form: AdminFormDefinition | null;
|
|
64
|
+
name: string;
|
|
65
|
+
setName: (value: string) => void;
|
|
66
|
+
slug: string;
|
|
67
|
+
setSlug: (value: string) => void;
|
|
68
|
+
mode: "builder" | "html";
|
|
69
|
+
onModeChange: (mode: "builder" | "html") => void;
|
|
70
|
+
html: string;
|
|
71
|
+
setHtml: (html: string) => void;
|
|
72
|
+
copyEmbed: () => void;
|
|
73
|
+
copyFeedback: string | null;
|
|
74
|
+
answerColumns: FormAnswerColumn[];
|
|
75
|
+
fields: AdminFormField[];
|
|
76
|
+
setFields: (fields: AdminFormField[]) => void;
|
|
77
|
+
tab: "fields" | "submissions";
|
|
78
|
+
/** Navigates to this form's Fields or Submissions route — real `navigate()`, not local state
|
|
79
|
+
* (ADR-063: Submissions has its own independent fetch, so a tab switch is a genuine route
|
|
80
|
+
* change, the same idiom `Deployment.tsx`'s tab strip uses). Pushes a history entry (no
|
|
81
|
+
* `replace`) so browser back/forward move naturally between the two routes, unlike the
|
|
82
|
+
* `?tab=` screens' `replace: true` view-filter switches. */
|
|
83
|
+
onTabChange: (tab: "fields" | "submissions") => void;
|
|
84
|
+
error: string | null;
|
|
85
|
+
saving: boolean;
|
|
86
|
+
/** Already-persisted field ids on the loaded form — see `existingFieldIdsOf`. */
|
|
87
|
+
existingFieldIds: string[];
|
|
88
|
+
/** Tabs render only for an existing, loaded form — never for `isNew`, and never before `form`
|
|
89
|
+
* has loaded. */
|
|
90
|
+
showTabs: boolean;
|
|
91
|
+
/** Per-tab-button refs, indexed the same as `FORM_TABS` — attach via each button's own `ref`
|
|
92
|
+
* callback in the view. */
|
|
93
|
+
tabRefs: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
|
94
|
+
/** Roving-tabindex keydown handler for the tablist `<div>` — ArrowLeft/ArrowRight/Home/End move
|
|
95
|
+
* both the selected tab and DOM focus together. */
|
|
96
|
+
onTabsKeyDown: (e: React.KeyboardEvent) => void;
|
|
97
|
+
handleSave: () => void;
|
|
98
|
+
handleStatusToggle: () => void;
|
|
99
|
+
/** Bound translator — see this file's own header for why it arrives via the hook rather than
|
|
100
|
+
* `FormEditor.tsx` calling `useAdminLocale()`/`FORMS_DICT` directly. */
|
|
101
|
+
t: (key: string) => string;
|
|
102
|
+
}
|
|
103
|
+
export declare function useFormEditor(props: {
|
|
104
|
+
formId: string;
|
|
105
|
+
tab: "fields" | "submissions";
|
|
106
|
+
port: AdminFormsPort;
|
|
107
|
+
navigate: (path: string) => void;
|
|
108
|
+
t: (key: string) => string;
|
|
109
|
+
}, { clipboard }?: {
|
|
110
|
+
clipboard?: {
|
|
111
|
+
writeText: (text: string) => Promise<void>;
|
|
112
|
+
};
|
|
113
|
+
}): FormEditorController;
|
|
114
|
+
export declare function useWiredFormEditor(props: {
|
|
115
|
+
formId: string;
|
|
116
|
+
tab: "fields" | "submissions";
|
|
117
|
+
}, _optional?: {}): FormEditorController;
|
|
118
|
+
//# sourceMappingURL=use-form-editor.hooks.d.ts.map
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { formView } from "../../models.js";
|
|
2
|
+
import { useFormsPorts, useFormsOptions } from "./FormsPorts.hooks.js";
|
|
3
|
+
import { useController } from "../../../core/react/use-controller.js";
|
|
4
|
+
import { createFormEditorController, initialFormEditorState } from "../../controllers/form-editor.controller.js";
|
|
5
|
+
import { formHtmlEmbed, formAnswerColumns } from "../../html-rules.js";
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
import { useFetchMutation, useFetchQuery } from "@jini-ai/ui/fetch-query";
|
|
8
|
+
import { FORM_TABS, KEYS, existingFieldIdsOf, nextTabIndex, visibleFormEditorError } from "../../rules.js";
|
|
9
|
+
export function useFormEditor(props, { clipboard } = {}) {
|
|
10
|
+
const { port, navigate, t } = props;
|
|
11
|
+
const { tab } = props;
|
|
12
|
+
const isNew = props.formId === "new";
|
|
13
|
+
const { controller: stateController, snapshot: stateSnapshot } = useController({ create: () => createFormEditorController({}), dependencies: [] });
|
|
14
|
+
const state = stateSnapshot ?? initialFormEditorState({});
|
|
15
|
+
const form = state.form;
|
|
16
|
+
const setForm = (value) => stateController?.update({ key: "form", value });
|
|
17
|
+
const mode = state.mode;
|
|
18
|
+
const html = state.html;
|
|
19
|
+
const setHtml = (value) => stateController?.update({ key: "html", value });
|
|
20
|
+
const copyFeedback = state.copyFeedback;
|
|
21
|
+
const setCopyFeedback = (value) => stateController?.update({ key: "copyFeedback", value });
|
|
22
|
+
const name = state.name;
|
|
23
|
+
const setName = (value) => stateController?.update({ key: "name", value });
|
|
24
|
+
const slug = state.slug;
|
|
25
|
+
const setSlug = (value) => stateController?.update({ key: "slug", value });
|
|
26
|
+
const fields = state.fields;
|
|
27
|
+
const setFields = (value) => stateController?.update({ key: "fields", value });
|
|
28
|
+
// No UI edits this (owner ask 2026-09-22 removed the notify checkbox/recipients input from the
|
|
29
|
+
// Fields tab — a future outside mail integration will design its own way to set it). Seeded from
|
|
30
|
+
// the loaded form and carried through unchanged on every save, so an operator saving a form never
|
|
31
|
+
// wipes out a `notify` setting that predates this UI removal. Lives in the section controller, not a
|
|
32
|
+
// `const`, only so `handleSave`'s existing-form branch can resync it from the write's own response
|
|
33
|
+
// exactly like `form`/`name`/`slug`/`fields` already do below.
|
|
34
|
+
const notify = state.notify;
|
|
35
|
+
// Roving-tabindex focus targets for the tab strip below, indexed the same as `FORM_TABS` — see
|
|
36
|
+
// `nextTabIndex`'s doc comment for why the index math itself lives outside the component.
|
|
37
|
+
const tabRefs = useRef([]);
|
|
38
|
+
const list = useFetchQuery({ key: KEYS.form(props.formId), fetch: () => port.getFormDefinition({ id: props.formId }).then(form => ({ data: formView({ form }) })) }, { enabled: !isNew });
|
|
39
|
+
// Seeds `form`/`name`/`slug`/`fields`/`notify` from `list.data` exactly once per `formId` — see
|
|
40
|
+
// this file's own header for the regression this guards against (a background refetch of the SAME
|
|
41
|
+
// formId must not clobber in-progress edits).
|
|
42
|
+
const seededFormIdRef = useRef(null);
|
|
43
|
+
const seededControllerRef = useRef(null);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (seededControllerRef.current !== stateController) {
|
|
46
|
+
seededControllerRef.current = stateController;
|
|
47
|
+
seededFormIdRef.current = null;
|
|
48
|
+
}
|
|
49
|
+
if (seededFormIdRef.current !== props.formId)
|
|
50
|
+
seededFormIdRef.current = null;
|
|
51
|
+
if (!stateController)
|
|
52
|
+
return;
|
|
53
|
+
if (isNew || list.status === "loading" || !list.data)
|
|
54
|
+
return;
|
|
55
|
+
if (seededFormIdRef.current === props.formId)
|
|
56
|
+
return;
|
|
57
|
+
seededFormIdRef.current = props.formId;
|
|
58
|
+
const loaded = list.data.data;
|
|
59
|
+
stateController.seed({ form: loaded });
|
|
60
|
+
}, [props.formId, isNew, list.status, list.data, stateController]);
|
|
61
|
+
// None of these invalidate `KEYS.form(props.formId)` — only `KEYS.list`. `handleSave`/
|
|
62
|
+
// `handleStatusToggle` below already set `form` (and, for save, the rest of the seeded fields)
|
|
63
|
+
// directly from each mutation's own response, so invalidating this hook's OWN read key would only
|
|
64
|
+
// buy a redundant background refetch of data already in hand — the same reasoning
|
|
65
|
+
// `use-collection-entry-editor.hooks.ts`'s `save()`/`toggleLifecycle()` document for why THEIR
|
|
66
|
+
// `invalidates` names only the sibling `KEYS.entries(...)`, never their own `KEYS.entry(...)`.
|
|
67
|
+
const updateMutation = useFetchMutation({
|
|
68
|
+
run: ({ input }) =>
|
|
69
|
+
// Targets the loaded record's real id, never `props.formId` directly — the admin URL now
|
|
70
|
+
// carries the form's slug when one resolves (ui-fixes-backlog.md #8), so `props.formId` may
|
|
71
|
+
// itself BE that slug. `form.id` is always the real id regardless of which one the URL held,
|
|
72
|
+
// which is what lets the PUT route stay id-only (no matching slug support needed — see
|
|
73
|
+
// `get-by-id.ts`'s own comment on why only the GET route resolves either). The `?? props
|
|
74
|
+
// .formId` fallback only matters if this ever fired before `form` loaded, which it can't:
|
|
75
|
+
// `FormEditor.tsx`'s `!isNew && !form` guards keep the whole editor (Save button included)
|
|
76
|
+
// off-screen until `form` is set.
|
|
77
|
+
port.updateFormDefinition({ id: form?.id ?? props.formId }, input),
|
|
78
|
+
}, {
|
|
79
|
+
invalidates: [KEYS.list],
|
|
80
|
+
});
|
|
81
|
+
const createMutation = useFetchMutation({
|
|
82
|
+
run: ({ input }) => port.createFormDefinition({ name: input.name, slug: input.slug, fields: input.fields, ...(input.mode ? { mode: input.mode, html: input.html } : {}) }, { notify: input.notify }),
|
|
83
|
+
}, {
|
|
84
|
+
invalidates: [KEYS.list],
|
|
85
|
+
});
|
|
86
|
+
const statusMutation = useFetchMutation({
|
|
87
|
+
run: ({ input }) => port.updateFormDefinition({ id: input.id }, { status: input.status }),
|
|
88
|
+
}, {
|
|
89
|
+
invalidates: [KEYS.list],
|
|
90
|
+
});
|
|
91
|
+
function onModeChange(next) {
|
|
92
|
+
stateController?.changeMode({ mode: next }, { submitLabel: t("Send") });
|
|
93
|
+
}
|
|
94
|
+
async function copyEmbed() {
|
|
95
|
+
if (!form)
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
await (clipboard ?? navigator.clipboard).writeText(formHtmlEmbed({ slug: form.slug }));
|
|
99
|
+
setCopyFeedback(t("Copied!"));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
setCopyFeedback(t("Could not copy embed"));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function handleSave() {
|
|
106
|
+
// Ordinary Builder saves keep their existing endpoint/permissions. A mode change is explicit.
|
|
107
|
+
const authoring = mode === "html" ? { mode, html } : form?.mode === "html" ? { mode } : {};
|
|
108
|
+
try {
|
|
109
|
+
if (isNew) {
|
|
110
|
+
// `notify` here is always this hook's own blank default — a new form has no earlier value
|
|
111
|
+
// to carry through, and there is no UI on this screen to set one instead.
|
|
112
|
+
const created = await createMutation.mutate({ input: { name, slug, fields, notify, ...authoring } });
|
|
113
|
+
// Slug, not id — see this hook's own file header / `get-by-id.ts` for the id-or-slug
|
|
114
|
+
// resolution this now lands on (ui-fixes-backlog.md #8).
|
|
115
|
+
navigate(`/forms/${created.slug}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// `notify` is the value this hook seeded from the loaded form and has never changed since —
|
|
119
|
+
// sent back unchanged so saving name/slug/field edits can never wipe a stored notify setting
|
|
120
|
+
// (see this hook's own `notify` state comment).
|
|
121
|
+
// Set directly from the write's own response — `updateMutation` doesn't invalidate this
|
|
122
|
+
// hook's own `KEYS.form(id)` read (see the mutations' own comment above), so there is no
|
|
123
|
+
// background refetch to wait for or to accidentally clobber an in-progress edit with.
|
|
124
|
+
// Mirrors `use-collection-entry-editor.hooks.ts`'s `save()`.
|
|
125
|
+
const updatedCore = await updateMutation.mutate({ input: { name, fields, notify, ...authoring } });
|
|
126
|
+
const updated = formView({ form: updatedCore });
|
|
127
|
+
stateController?.seed({ form: updated });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// already surfaced through updateMutation.error/createMutation.error -> error below
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function handleStatusToggle() {
|
|
135
|
+
if (!form)
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
const updatedCore = await statusMutation.mutate({ input: {
|
|
139
|
+
id: form.id,
|
|
140
|
+
status: form.status === "active" ? "disabled" : "active",
|
|
141
|
+
} });
|
|
142
|
+
setForm(formView({ form: updatedCore }));
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// already surfaced through statusMutation.error -> error below
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const saving = updateMutation.status === "pending" || createMutation.status === "pending" || statusMutation.status === "pending";
|
|
149
|
+
const error = visibleFormEditorError({
|
|
150
|
+
updateError: updateMutation.error,
|
|
151
|
+
createError: createMutation.error,
|
|
152
|
+
statusError: statusMutation.error,
|
|
153
|
+
listError: list.error,
|
|
154
|
+
hasForm: form !== null,
|
|
155
|
+
saveFallback: t("save failed"),
|
|
156
|
+
statusUpdateFallback: t("status update failed"),
|
|
157
|
+
loadFormFallback: t("failed to load form"),
|
|
158
|
+
});
|
|
159
|
+
// Navigates to `/forms/:formId` or `/forms/:formId/submissions` (ADR-063) — `panels.tsx`'s
|
|
160
|
+
// `forms` panel keys `FormEditor` off `ctx.params.formId` on BOTH routes, so this is a route
|
|
161
|
+
// change, not a remount: in-progress Fields edits survive a trip to Submissions and back.
|
|
162
|
+
function onTabChange(nextTab) {
|
|
163
|
+
navigate(nextTab === "submissions" ? `/forms/${props.formId}/submissions` : `/forms/${props.formId}`);
|
|
164
|
+
}
|
|
165
|
+
function onTabsKeyDown(e) {
|
|
166
|
+
const currentIndex = FORM_TABS.findIndex((t) => t.id === tab);
|
|
167
|
+
const index = nextTabIndex({ key: e.key, currentIndex });
|
|
168
|
+
if (index === null)
|
|
169
|
+
return;
|
|
170
|
+
e.preventDefault();
|
|
171
|
+
onTabChange(FORM_TABS[index].id);
|
|
172
|
+
tabRefs.current[index]?.focus();
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
isNew,
|
|
176
|
+
form,
|
|
177
|
+
mode, onModeChange, html, setHtml, copyEmbed, copyFeedback,
|
|
178
|
+
answerColumns: formAnswerColumns({ fields: form?.fields ?? [] }),
|
|
179
|
+
name,
|
|
180
|
+
setName,
|
|
181
|
+
slug,
|
|
182
|
+
setSlug,
|
|
183
|
+
fields,
|
|
184
|
+
setFields,
|
|
185
|
+
tab,
|
|
186
|
+
onTabChange,
|
|
187
|
+
error,
|
|
188
|
+
saving,
|
|
189
|
+
existingFieldIds: existingFieldIdsOf({ form }),
|
|
190
|
+
showTabs: !isNew && form !== null,
|
|
191
|
+
tabRefs,
|
|
192
|
+
onTabsKeyDown,
|
|
193
|
+
handleSave,
|
|
194
|
+
handleStatusToggle,
|
|
195
|
+
t,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function useWiredFormEditor(props, _optional = {}) {
|
|
199
|
+
const { formsApi, formsNavigation } = useFormsPorts();
|
|
200
|
+
const { t, clipboard, adminBase } = useFormsOptions();
|
|
201
|
+
const navigate = (routePath) => {
|
|
202
|
+
if (!formsNavigation)
|
|
203
|
+
throw new Error('Missing forms navigation port');
|
|
204
|
+
formsNavigation.navigate({ base: adminBase, routePath });
|
|
205
|
+
};
|
|
206
|
+
return useFormEditor({ ...props, port: formsApi, navigate, t }, clipboard ? { clipboard } : {});
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=use-form-editor.hooks.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AdminFormField } from "../../models.js";
|
|
2
|
+
/**
|
|
3
|
+
* @file `FormFieldsEditor`'s own state (which row's attributes modal is open, plus the per-row
|
|
4
|
+
* kebab-trigger refs for WCAG focus-return), so the fields table in `FormEditor.tsx` is only
|
|
5
|
+
* markup.
|
|
6
|
+
*
|
|
7
|
+
* Extracted verbatim, including the `useRef` array — `Pattern 1` moves every `useRef` out of the
|
|
8
|
+
* component along with state/effects, not just `useState`. The field-list edits themselves
|
|
9
|
+
* (`updateField`/`addField`/`removeField`) are thin wrappers around `rules.ts`'s pure array
|
|
10
|
+
* transforms, applied through the parent-owned `onChange` (this component does not own `fields`
|
|
11
|
+
* itself — `FormEditor`'s own `fields` state does).
|
|
12
|
+
*
|
|
13
|
+
* Naming follows `hooks/use-settings-slice.hooks.ts`: `use-<thing>.hooks.ts`. Feature-local because
|
|
14
|
+
* nothing outside `features/forms` needs it.
|
|
15
|
+
*/
|
|
16
|
+
export interface FormFieldsEditorController {
|
|
17
|
+
/** Which field's `FieldAttributesDialog` is open, by index — `null` when none is. */
|
|
18
|
+
editingAttrsIndex: number | null;
|
|
19
|
+
openAttrsDialog: (index: number) => void;
|
|
20
|
+
/** Closes the attributes dialog and returns focus to the kebab trigger that opened it (WCAG 2.1
|
|
21
|
+
* AA "focus returns to trigger element when modal closes"). */
|
|
22
|
+
closeAttrsDialog: () => void;
|
|
23
|
+
/** Per-row kebab-trigger refs, indexed the same as `fields` — attach via each row's own `ref`
|
|
24
|
+
* callback in the view. */
|
|
25
|
+
kebabRefs: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
|
26
|
+
updateField: (index: number, patch: Partial<AdminFormField>) => void;
|
|
27
|
+
addField: () => void;
|
|
28
|
+
removeField: (index: number) => void;
|
|
29
|
+
}
|
|
30
|
+
export declare function useFormFieldsEditor(props: {
|
|
31
|
+
fields: AdminFormField[];
|
|
32
|
+
onChange: (fields: AdminFormField[]) => void;
|
|
33
|
+
}, _optional?: Record<string, never>): FormFieldsEditorController;
|
|
34
|
+
//# sourceMappingURL=use-form-fields-editor.hooks.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useController } from "../../../core/react/use-controller.js";
|
|
2
|
+
import { createFormFieldsEditorController, initialFormFieldsEditorState } from "../../controllers/form-fields-editor.controller.js";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { addFormField, removeFormField, updateFormField } from "../../rules.js";
|
|
5
|
+
export function useFormFieldsEditor(props, _optional = {}) {
|
|
6
|
+
const { controller: stateController, snapshot: stateSnapshot } = useController({ create: () => createFormFieldsEditorController({}), dependencies: [] });
|
|
7
|
+
const state = stateSnapshot ?? initialFormFieldsEditorState({});
|
|
8
|
+
const editingAttrsIndex = state.editingAttrsIndex;
|
|
9
|
+
const setEditingAttrsIndex = (value) => stateController?.update({ key: "editingAttrsIndex", value });
|
|
10
|
+
const kebabRefs = useRef([]);
|
|
11
|
+
function closeAttrsDialog() {
|
|
12
|
+
const index = editingAttrsIndex;
|
|
13
|
+
setEditingAttrsIndex(null);
|
|
14
|
+
if (index !== null)
|
|
15
|
+
kebabRefs.current[index]?.focus();
|
|
16
|
+
}
|
|
17
|
+
function updateField(index, patch) {
|
|
18
|
+
props.onChange(updateFormField({ fields: props.fields, index, patch }));
|
|
19
|
+
}
|
|
20
|
+
function addField() {
|
|
21
|
+
props.onChange(addFormField({ fields: props.fields }));
|
|
22
|
+
}
|
|
23
|
+
function removeField(index) {
|
|
24
|
+
props.onChange(removeFormField({ fields: props.fields, index }));
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
editingAttrsIndex,
|
|
28
|
+
openAttrsDialog: setEditingAttrsIndex,
|
|
29
|
+
closeAttrsDialog,
|
|
30
|
+
kebabRefs,
|
|
31
|
+
updateField,
|
|
32
|
+
addField,
|
|
33
|
+
removeField,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=use-form-fields-editor.hooks.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Bind submission timestamps to the same operator locale and local time as the Forms list. */
|
|
2
|
+
export declare function useFormSubmissionDate(_required: Record<string, never>, { locale: localeOverride, timeZone }?: {
|
|
3
|
+
locale?: string;
|
|
4
|
+
timeZone?: string;
|
|
5
|
+
}): ({ iso }: {
|
|
6
|
+
iso: string;
|
|
7
|
+
}, _optional?: Record<string, never>) => {
|
|
8
|
+
text: string;
|
|
9
|
+
full: string;
|
|
10
|
+
dateTime: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=use-form-submission-date.hooks.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useFormsOptions } from "./FormsPorts.hooks.js";
|
|
2
|
+
import { formDateDisplay } from "../../rules.js";
|
|
3
|
+
/** Bind submission timestamps to the same operator locale and local time as the Forms list. */
|
|
4
|
+
export function useFormSubmissionDate(_required, { locale: localeOverride, timeZone } = {}) {
|
|
5
|
+
const { locale } = useFormsOptions();
|
|
6
|
+
// One locale subscription per submissions panel, rather than one per timestamp cell.
|
|
7
|
+
return ({ iso }, _optional = {}) => formDateDisplay({ iso, locale: localeOverride ?? locale }, timeZone === undefined ? {} : { timeZone });
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=use-form-submission-date.hooks.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AdminFormsPort } from "../../../core/ports/forms.js";
|
|
2
|
+
import type { FormsTrashPort } from "../../ports.js";
|
|
3
|
+
import type { AdminFormSubmission } from "../../models.js";
|
|
4
|
+
/**
|
|
5
|
+
* @file `FormSubmissionDetail`'s own state and delete action, so the detail view in
|
|
6
|
+
* `FormEditor.tsx` is only markup.
|
|
7
|
+
*
|
|
8
|
+
* Permanent delete now confirms through a modal (S5 fix, 2026-09-20), not an inline two-click
|
|
9
|
+
* button: the old `confirming`/`handleDelete` shape flipped a click's own label to "Confirm
|
|
10
|
+
* delete", so a real double-click deleted the submission outright with no way to back out — the
|
|
11
|
+
* same bug widgets' `trashOrPurge` had before its own `pendingPurge`/`ConfirmDialog` fix. This
|
|
12
|
+
* hook now mirrors that shape one-for-one: `requestDelete` only opens the dialog (no network),
|
|
13
|
+
* `cancelDelete` closes it with no request, and `confirmDelete` runs the mutation.
|
|
14
|
+
*
|
|
15
|
+
* Naming follows `hooks/use-settings-slice.hooks.ts`: `use-<thing>.hooks.ts`. Feature-local because
|
|
16
|
+
* nothing outside `features/forms` needs it.
|
|
17
|
+
*
|
|
18
|
+
* `port` is injected — see `core/ports/forms.ts` (shared with `use-form-
|
|
19
|
+
* submissions.hooks.ts`, since both read/write the same submissions list for a form) — rather than
|
|
20
|
+
* importing `lib/api` directly, so a test can describe load/delete outcomes against
|
|
21
|
+
* `the memory adapter` instead of stubbing global `fetch`.
|
|
22
|
+
* `useWiredFormSubmissionDetail` below is the zero-argument pair `FormEditor.tsx` actually mounts.
|
|
23
|
+
*
|
|
24
|
+
* `@jini-ai/ui/fetch-query` migration (2026-08-12): the load is one `useFetchQuery` keyed on
|
|
25
|
+
* `KEYS.submissionDetail(formId, submissionId)` — a query cannot commit a response belonging to a
|
|
26
|
+
* prior key, which eliminates the load race an external audit flagged at this file's old line 48
|
|
27
|
+
* (a plain `.then()`/`.catch()` effect with no cancellation guard) by construction. `confirmDelete`
|
|
28
|
+
* is a `useFetchMutation` that `invalidates: [KEYS.submissionsList(formId)]`, so the sibling list
|
|
29
|
+
* (`use-form-submissions.hooks.ts`) refreshes on its own — `props.onDeleted()` now only needs to
|
|
30
|
+
* clear the caller's `selectedId` (a UI-navigation concern this hook can't own), not also trigger a
|
|
31
|
+
* reload, so `FormEditor.tsx`'s `onDeleted` callback drops its own `load()` call.
|
|
32
|
+
*/
|
|
33
|
+
export interface FormSubmissionDetailController {
|
|
34
|
+
/** `null` until the initial load settles — the caller renders a loading state. */
|
|
35
|
+
submission: AdminFormSubmission | null;
|
|
36
|
+
error: string | null;
|
|
37
|
+
/** True while the "Delete permanently?" confirm dialog should be open — opened by
|
|
38
|
+
* {@link FormSubmissionDetailController.requestDelete}, closed by
|
|
39
|
+
* {@link FormSubmissionDetailController.cancelDelete} or once
|
|
40
|
+
* {@link FormSubmissionDetailController.confirmDelete} settles. */
|
|
41
|
+
confirmOpen: boolean;
|
|
42
|
+
deleting: boolean;
|
|
43
|
+
/** Opens the confirm dialog. No network call — a click alone can never delete. */
|
|
44
|
+
requestDelete: () => void;
|
|
45
|
+
/** Closes the confirm dialog with no request. */
|
|
46
|
+
cancelDelete: () => void;
|
|
47
|
+
/** Runs the delete. On success, calls `props.onDeleted()`. Closes the dialog in `finally`
|
|
48
|
+
* either way, so a failed delete doesn't leave the operator stuck behind it — the failure is
|
|
49
|
+
* still visible via `error` below. */
|
|
50
|
+
confirmDelete: () => Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
export declare function useFormSubmissionDetail(props: {
|
|
53
|
+
port: AdminFormsPort;
|
|
54
|
+
trash: FormsTrashPort;
|
|
55
|
+
formId: string;
|
|
56
|
+
submissionId: string;
|
|
57
|
+
onDeleted: () => void;
|
|
58
|
+
/** Bound translator — see `use-forms-list.hooks.ts`'s own header for why this arrives via
|
|
59
|
+
* injection rather than this hook calling `useAdminLocale()` itself. Optional (defaults to
|
|
60
|
+
* identity) so existing callers/tests that don't pass one keep seeing the raw English copy. */
|
|
61
|
+
t?: (key: string) => string;
|
|
62
|
+
}, _optional?: Record<string, never>): FormSubmissionDetailController;
|
|
63
|
+
export declare function useWiredFormSubmissionDetail(props: {
|
|
64
|
+
formId: string;
|
|
65
|
+
submissionId: string;
|
|
66
|
+
onDeleted: () => void;
|
|
67
|
+
}, _optional?: {}): FormSubmissionDetailController;
|
|
68
|
+
//# sourceMappingURL=use-form-submission-detail.hooks.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { submissionView } from "../../models.js";
|
|
2
|
+
import { useFormsPorts, useFormsOptions } from "./FormsPorts.hooks.js";
|
|
3
|
+
import { useController } from "../../../core/react/use-controller.js";
|
|
4
|
+
import { createFormSubmissionDetailController, initialFormSubmissionDetailState } from "../../controllers/form-submission-detail.controller.js";
|
|
5
|
+
import { describeApiError as describeCoreApiError } from "../../../core/transport/errors.js";
|
|
6
|
+
const describeApiError = (e, fallback) => describeCoreApiError({ e, fallback });
|
|
7
|
+
import { useFetchMutation, useFetchQuery, useInvalidate } from "@jini-ai/ui/fetch-query";
|
|
8
|
+
import { KEYS, describeTrashError } from "../../rules.js";
|
|
9
|
+
export function useFormSubmissionDetail(props, _optional = {}) {
|
|
10
|
+
const { port, trash } = props;
|
|
11
|
+
const t = props.t ?? ((key) => key);
|
|
12
|
+
const { controller: stateController, snapshot: stateSnapshot } = useController({ create: () => createFormSubmissionDetailController({}), dependencies: [] });
|
|
13
|
+
const state = stateSnapshot ?? initialFormSubmissionDetailState({});
|
|
14
|
+
const confirmOpen = state.confirmOpen;
|
|
15
|
+
const setConfirmOpen = (value) => stateController?.update({ key: "confirmOpen", value });
|
|
16
|
+
const invalidate = useInvalidate();
|
|
17
|
+
const list = useFetchQuery({
|
|
18
|
+
key: KEYS.submissionDetail(props.formId, props.submissionId),
|
|
19
|
+
fetch: () => port.getFormSubmission({ formId: props.formId, submissionId: props.submissionId }).then(submission => ({ data: submissionView({ submission }) })),
|
|
20
|
+
});
|
|
21
|
+
const deleteMutation = useFetchMutation({
|
|
22
|
+
run: ({ input: _ }) => trash.trash({ type: "form_submission", id: props.submissionId }),
|
|
23
|
+
}, {
|
|
24
|
+
invalidates: [KEYS.submissionsList(props.formId)],
|
|
25
|
+
});
|
|
26
|
+
function requestDelete() {
|
|
27
|
+
setConfirmOpen(true);
|
|
28
|
+
}
|
|
29
|
+
function cancelDelete() {
|
|
30
|
+
setConfirmOpen(false);
|
|
31
|
+
}
|
|
32
|
+
/** T7a (2026-09-21): the confirmation mutation moves the submission to Trash through the generic
|
|
33
|
+
* `POST /trash/items` endpoint — see `adapters/http.ts`'s own doc. A 404
|
|
34
|
+
* (`describeTrashError`'s `alreadyGone`) means the submission is already gone: from
|
|
35
|
+
* the operator's point of view that's the same outcome as a successful delete, so this still calls
|
|
36
|
+
* `onDeleted()` and invalidates the list directly — `deleteMutation`'s own `invalidates` only fires
|
|
37
|
+
* on success, and a failed mutation would otherwise leave the sibling list showing a row that's
|
|
38
|
+
* already gone. */
|
|
39
|
+
async function confirmDelete() {
|
|
40
|
+
try {
|
|
41
|
+
await deleteMutation.mutate({ input: undefined });
|
|
42
|
+
props.onDeleted();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
if (describeTrashError({ error: e instanceof Error ? e : null, fallback: "delete failed", versionChangedMessage: t("This item changed since you loaded it. Reload and try again.") }).alreadyGone) {
|
|
46
|
+
invalidate({ key: KEYS.submissionsList(props.formId) });
|
|
47
|
+
props.onDeleted();
|
|
48
|
+
}
|
|
49
|
+
// otherwise: already surfaced through deleteMutation.error -> error below
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
setConfirmOpen(false);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const submission = list.data?.data ?? null;
|
|
56
|
+
// The delete's own failure outranks a background load-refresh failure — flat `if`s rather than a
|
|
57
|
+
// nested ternary, per `@jini-ai/ui/fetch-query`'s `resolveFetchQueryStatus` doc on why the two carry
|
|
58
|
+
// a different complexity-gate weight for the same branch count.
|
|
59
|
+
let error = null;
|
|
60
|
+
if (deleteMutation.error) {
|
|
61
|
+
error = describeTrashError({ error: deleteMutation.error, fallback: "delete failed", versionChangedMessage: t("This item changed since you loaded it. Reload and try again.") }).message;
|
|
62
|
+
}
|
|
63
|
+
else if (list.error) {
|
|
64
|
+
error = describeApiError(list.error, "failed to load submission");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
submission,
|
|
68
|
+
error,
|
|
69
|
+
confirmOpen,
|
|
70
|
+
deleting: deleteMutation.status === "pending",
|
|
71
|
+
requestDelete,
|
|
72
|
+
cancelDelete,
|
|
73
|
+
confirmDelete,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function useWiredFormSubmissionDetail(props, _optional = {}) {
|
|
77
|
+
const { formsApi, formsTrash } = useFormsPorts();
|
|
78
|
+
const { t } = useFormsOptions();
|
|
79
|
+
return useFormSubmissionDetail({ ...props, port: formsApi, trash: formsTrash, t });
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=use-form-submission-detail.hooks.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AdminFormsPort } from "../../../core/ports/forms.js";
|
|
2
|
+
import type { AdminFormSubmission } from "../../models.js";
|
|
3
|
+
/**
|
|
4
|
+
* @file `FormSubmissions`'s own state and paginated load, so the submissions list in
|
|
5
|
+
* `FormEditor.tsx` is only markup.
|
|
6
|
+
*
|
|
7
|
+
* Moved from the host — same cursor-append load shape, same error strings. `load` keeps its original
|
|
8
|
+
* `(cursor?: string) => void` signature rather than being split into separate "load"/"loadMore"
|
|
9
|
+
* functions, so both call sites (`load(nextCursor)` for "Load more", `load()` after a submission
|
|
10
|
+
* delete) carry over unchanged.
|
|
11
|
+
*
|
|
12
|
+
* Naming follows `hooks/use-settings-slice.hooks.ts`: `use-<thing>.hooks.ts`. Feature-local because
|
|
13
|
+
* nothing outside `features/forms` needs it.
|
|
14
|
+
*
|
|
15
|
+
* `port` is injected — see `core/ports/forms.ts` (shared with `use-form-submission-
|
|
16
|
+
* detail.hooks.ts`, since both read/write the same submissions list for a form) — rather than
|
|
17
|
+
* importing `lib/api` directly, so a test can describe list outcomes against
|
|
18
|
+
* `the memory adapter` instead of stubbing global `fetch`. `useWiredFormSubmissions`
|
|
19
|
+
* below is the zero-argument pair `FormEditor.tsx` actually mounts.
|
|
20
|
+
*
|
|
21
|
+
* `@jini-ai/ui/fetch-query` migration (2026-08-12): the FIRST page is one `useFetchQuery` keyed on
|
|
22
|
+
* `KEYS.submissionsList(formId)` — a query cannot commit a response belonging to a prior key, which
|
|
23
|
+
* eliminates the load race an external audit flagged at this file's old line 56 (a plain
|
|
24
|
+
* `.then()`/`.catch()` effect with no cancellation guard) for that page. Subsequent "Load more"
|
|
25
|
+
* pages are NOT folded into that same query: `@jini-ai/ui/fetch-query`'s `QueryKey` doc binds one
|
|
26
|
+
* hook to one FIXED key, and a cursor-appended page list is exactly the "moving-target key" shape
|
|
27
|
+
* that doc's own Skip precedent (`useSettingsContainer`, see this migration's dispatch brief) warns
|
|
28
|
+
* against faking — so accumulated pages live in the section controller, fetched directly through `port`
|
|
29
|
+
* (bypassing the cache, since an appended page is not a cacheable "the current state of X", it's an
|
|
30
|
+
* accumulating view built by this one screen). `pageSettlement` (`useSettlementGeneration`) guards
|
|
31
|
+
* that accumulation against the SAME class of race the base query gets for free: a `loadMore` in
|
|
32
|
+
* flight when `formId` changes, or when a first-page refetch (e.g. a delete's invalidate) resets
|
|
33
|
+
* the list, must not append its page once it resolves — see `resetMorePages` below, which mints a
|
|
34
|
+
* fresh generation on every reset so any older in-flight page fetch is superseded. A synchronous
|
|
35
|
+
* `loadingMoreRef` lock, checked before minting a generation, covers the other half: two `loadMore`
|
|
36
|
+
* calls issued in the SAME tick (a double click) must send only one request
|
|
37
|
+
* (2026-09-20, `plan-content2.md` #3/#5/S2).
|
|
38
|
+
*
|
|
39
|
+
* The audit's second, more serious finding at this file — `selectedId` surviving a `formId` change
|
|
40
|
+
* untouched, so a delete could fire against a submission id belonging to the PREVIOUS form while its
|
|
41
|
+
* stale rows were still on screen — is fixed by the identity-reset effect below, which clears both
|
|
42
|
+
* `selectedId` and the accumulated "more" pages whenever `formId` changes.
|
|
43
|
+
*/
|
|
44
|
+
export interface FormSubmissionsController {
|
|
45
|
+
/** `null` until the initial load settles — the caller renders a loading state. */
|
|
46
|
+
submissions: AdminFormSubmission[] | null;
|
|
47
|
+
nextCursor: string | null;
|
|
48
|
+
error: string | null;
|
|
49
|
+
/** The submission `FormSubmissions` is showing `FormSubmissionDetail` for — `null` shows the
|
|
50
|
+
* plain list. */
|
|
51
|
+
selectedId: string | null;
|
|
52
|
+
setSelectedId: (id: string | null) => void;
|
|
53
|
+
/** Re-fetches. Passing a cursor appends to `submissions`; omitting it replaces the list from the
|
|
54
|
+
* start (used after a submission is deleted, so the list reflects the removal). */
|
|
55
|
+
load: (cursor?: string) => void;
|
|
56
|
+
/** True while a "Load more" page fetch is in flight — drives the button's `disabled` and
|
|
57
|
+
* "Loading…" label in `FormEditor.tsx`. */
|
|
58
|
+
loadingMore: boolean;
|
|
59
|
+
}
|
|
60
|
+
export declare function useFormSubmissions(props: {
|
|
61
|
+
formId: string;
|
|
62
|
+
port: AdminFormsPort;
|
|
63
|
+
}, _optional?: {}): FormSubmissionsController;
|
|
64
|
+
export declare function useWiredFormSubmissions(props: {
|
|
65
|
+
formId: string;
|
|
66
|
+
}, _optional?: {}): FormSubmissionsController;
|
|
67
|
+
//# sourceMappingURL=use-form-submissions.hooks.d.ts.map
|