@pilotiq/pilotiq 0.23.1 → 0.24.2
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 +91 -0
- package/boost/guidelines.md +566 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/dist/actions/exportFactory.d.ts +10 -0
- package/dist/actions/exportFactory.d.ts.map +1 -1
- package/dist/actions/exportFactory.js +10 -0
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/react/CollabRoomContext.d.ts +5 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/routes/helpers.d.ts.map +1 -1
- package/dist/routes/helpers.js +6 -2
- package/dist/routes/helpers.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +12 -0
- package/dist/routes/relations.js.map +1 -1
- package/package.json +6 -1
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/dist/react/useCollabSeed.d.ts +0 -23
- package/dist/react/useCollabSeed.d.ts.map +0 -1
- package/dist/react/useCollabSeed.js +0 -82
- package/dist/react/useCollabSeed.js.map +0 -1
- package/src/Cluster.test.ts +0 -283
- package/src/Cluster.ts +0 -83
- package/src/Column.test.ts +0 -199
- package/src/Column.ts +0 -710
- package/src/Global.test.ts +0 -367
- package/src/Global.ts +0 -169
- package/src/Page.test.ts +0 -114
- package/src/Page.ts +0 -208
- package/src/Pilotiq.perf.test.ts +0 -252
- package/src/Pilotiq.test.ts +0 -129
- package/src/Pilotiq.ts +0 -1158
- package/src/PilotiqRegistry.ts +0 -36
- package/src/PilotiqServiceProvider.ts +0 -121
- package/src/RelationManager.test.ts +0 -400
- package/src/RelationManager.ts +0 -527
- package/src/RenderHook.test.ts +0 -252
- package/src/RenderHook.ts +0 -242
- package/src/Resource.test.ts +0 -284
- package/src/Resource.ts +0 -526
- package/src/RightPanel.test.ts +0 -202
- package/src/RightPanel.ts +0 -132
- package/src/Tab.test.ts +0 -91
- package/src/Tab.ts +0 -156
- package/src/UserMenuItem.ts +0 -145
- package/src/actions/Action.test.ts +0 -2526
- package/src/actions/Action.ts +0 -1515
- package/src/actions/ActionGroup.test.ts +0 -112
- package/src/actions/ActionGroup.ts +0 -173
- package/src/actions/attachFactory.ts +0 -172
- package/src/actions/bulkFactories.ts +0 -168
- package/src/actions/crudFactories.ts +0 -220
- package/src/actions/exportFactory.ts +0 -215
- package/src/actions/factoryHelpers.ts +0 -177
- package/src/actions/importFactory.ts +0 -243
- package/src/actions/index.ts +0 -17
- package/src/actions/m2mFactories.ts +0 -193
- package/src/actions/relationFactories.ts +0 -372
- package/src/applyPageHooks.test.ts +0 -463
- package/src/applyPageHooks.ts +0 -330
- package/src/authorization.test.ts +0 -483
- package/src/breadcrumbs.test.ts +0 -238
- package/src/cells/coerce.test.ts +0 -85
- package/src/cells/coerce.ts +0 -84
- package/src/clusterPaths.ts +0 -35
- package/src/columns/BadgeColumn.test.ts +0 -54
- package/src/columns/BadgeColumn.ts +0 -32
- package/src/columns/BooleanColumn.test.ts +0 -41
- package/src/columns/BooleanColumn.ts +0 -18
- package/src/columns/ColorColumn.test.ts +0 -37
- package/src/columns/ColorColumn.ts +0 -38
- package/src/columns/IconColumn.test.ts +0 -54
- package/src/columns/IconColumn.ts +0 -37
- package/src/columns/ImageColumn.test.ts +0 -41
- package/src/columns/ImageColumn.ts +0 -28
- package/src/columns/SelectColumn.ts +0 -98
- package/src/columns/TextColumn.test.ts +0 -190
- package/src/columns/TextColumn.ts +0 -20
- package/src/columns/TextInputColumn.ts +0 -68
- package/src/columns/ToggleColumn.ts +0 -46
- package/src/columns/editableColumns.test.ts +0 -238
- package/src/columns/index.ts +0 -9
- package/src/defaultGlobalPages.ts +0 -95
- package/src/defaultPages.test.ts +0 -634
- package/src/defaultPages.ts +0 -617
- package/src/defaultViewPage.test.ts +0 -147
- package/src/elements/Form.test.ts +0 -223
- package/src/elements/Form.ts +0 -416
- package/src/elements/ListTabs.ts +0 -28
- package/src/elements/Table.test.ts +0 -422
- package/src/elements/Table.ts +0 -850
- package/src/elements/TableGroup.test.ts +0 -260
- package/src/elements/TableGroup.ts +0 -334
- package/src/elements/dispatchAction.test.ts +0 -463
- package/src/elements/dispatchAction.ts +0 -355
- package/src/elements/dispatchForm.test.ts +0 -477
- package/src/elements/dispatchForm.ts +0 -1993
- package/src/elements/dispatchTable.test.ts +0 -1514
- package/src/elements/dispatchTable.ts +0 -745
- package/src/elements/index.ts +0 -21
- package/src/entries/BadgeEntry.ts +0 -39
- package/src/entries/CodeEntry.test.ts +0 -40
- package/src/entries/CodeEntry.ts +0 -52
- package/src/entries/ColorEntry.ts +0 -63
- package/src/entries/ComponentEntry.test.ts +0 -173
- package/src/entries/ComponentEntry.ts +0 -95
- package/src/entries/Entry.ts +0 -304
- package/src/entries/IconEntry.ts +0 -49
- package/src/entries/ImageEntry.ts +0 -61
- package/src/entries/KeyValueEntry.ts +0 -47
- package/src/entries/RepeatableEntry.test.ts +0 -239
- package/src/entries/RepeatableEntry.ts +0 -173
- package/src/entries/TextEntry.test.ts +0 -394
- package/src/entries/TextEntry.ts +0 -60
- package/src/entries/index.ts +0 -12
- package/src/entries/leaves.test.ts +0 -306
- package/src/entries/registry.ts +0 -54
- package/src/fields/BuilderField.test.ts +0 -1188
- package/src/fields/BuilderField.ts +0 -605
- package/src/fields/BuilderRelationship.test.ts +0 -811
- package/src/fields/CheckboxField.test.ts +0 -44
- package/src/fields/CheckboxField.ts +0 -27
- package/src/fields/CheckboxListField.test.ts +0 -99
- package/src/fields/CheckboxListField.ts +0 -66
- package/src/fields/ColorPickerField.test.ts +0 -33
- package/src/fields/ColorPickerField.ts +0 -25
- package/src/fields/DateField.ts +0 -54
- package/src/fields/DateTimeField.test.ts +0 -55
- package/src/fields/EmailField.ts +0 -16
- package/src/fields/Field.test.ts +0 -654
- package/src/fields/Field.ts +0 -817
- package/src/fields/FileUploadField.test.ts +0 -143
- package/src/fields/FileUploadField.ts +0 -159
- package/src/fields/HiddenField.test.ts +0 -27
- package/src/fields/HiddenField.ts +0 -28
- package/src/fields/KeyValueField.test.ts +0 -105
- package/src/fields/KeyValueField.ts +0 -55
- package/src/fields/MarkdownField.test.ts +0 -167
- package/src/fields/MarkdownField.ts +0 -162
- package/src/fields/NumberField.ts +0 -33
- package/src/fields/RadioField.test.ts +0 -94
- package/src/fields/RadioField.ts +0 -67
- package/src/fields/RepeaterField.test.ts +0 -1806
- package/src/fields/RepeaterField.ts +0 -939
- package/src/fields/RepeaterRelationship.test.ts +0 -1923
- package/src/fields/RepeaterSimple.test.ts +0 -248
- package/src/fields/RowButton.test.ts +0 -219
- package/src/fields/RowButton.ts +0 -135
- package/src/fields/SelectField.test.ts +0 -192
- package/src/fields/SelectField.ts +0 -235
- package/src/fields/SliderField.test.ts +0 -50
- package/src/fields/SliderField.ts +0 -53
- package/src/fields/SlugField.ts +0 -24
- package/src/fields/TagsInputField.test.ts +0 -154
- package/src/fields/TagsInputField.ts +0 -133
- package/src/fields/TextField.test.ts +0 -213
- package/src/fields/TextField.ts +0 -177
- package/src/fields/TextareaField.test.ts +0 -58
- package/src/fields/TextareaField.ts +0 -59
- package/src/fields/ToggleButtonsField.test.ts +0 -106
- package/src/fields/ToggleButtonsField.ts +0 -59
- package/src/fields/ToggleField.ts +0 -16
- package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
- package/src/fields/optionsResolver.ts +0 -95
- package/src/fields/resolveField.ts +0 -28
- package/src/filters/BooleanFilter.ts +0 -35
- package/src/filters/DateRangeFilter.test.ts +0 -194
- package/src/filters/DateRangeFilter.ts +0 -148
- package/src/filters/Filter.test.ts +0 -268
- package/src/filters/Filter.ts +0 -184
- package/src/filters/FormFilter.test.ts +0 -238
- package/src/filters/FormFilter.ts +0 -215
- package/src/filters/MultiSelectFilter.test.ts +0 -119
- package/src/filters/MultiSelectFilter.ts +0 -78
- package/src/filters/QueryBuilderFilter.test.ts +0 -662
- package/src/filters/QueryBuilderFilter.ts +0 -398
- package/src/filters/SelectFilter.ts +0 -46
- package/src/filters/TernaryFilter.test.ts +0 -160
- package/src/filters/TernaryFilter.ts +0 -72
- package/src/filters/TrashedFilter.test.ts +0 -149
- package/src/filters/TrashedFilter.ts +0 -55
- package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
- package/src/filters/queryBuilder/Constraint.ts +0 -115
- package/src/filters/queryBuilder/DateConstraint.ts +0 -69
- package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
- package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
- package/src/filters/queryBuilder/TextConstraint.ts +0 -64
- package/src/filters/queryBuilder/index.ts +0 -12
- package/src/icons/index.ts +0 -2
- package/src/icons/lucide.ts +0 -204
- package/src/icons/registry.test.ts +0 -56
- package/src/icons/registry.ts +0 -41
- package/src/icons/types.ts +0 -47
- package/src/index.ts +0 -525
- package/src/io/csv.test.ts +0 -142
- package/src/io/csv.ts +0 -170
- package/src/nestedRelationManagerData.test.ts +0 -547
- package/src/notifications/Notification.test.ts +0 -210
- package/src/notifications/Notification.ts +0 -354
- package/src/notifications/broadcast.test.ts +0 -110
- package/src/notifications/broadcast.ts +0 -95
- package/src/notifications/database.test.ts +0 -383
- package/src/notifications/database.ts +0 -398
- package/src/notifications/databaseNotifications.test.ts +0 -187
- package/src/notifications/dispatchNotificationAction.test.ts +0 -341
- package/src/notifications/dispatchNotificationAction.ts +0 -142
- package/src/notifications/flash.test.ts +0 -89
- package/src/notifications/flash.ts +0 -71
- package/src/notifications/index.ts +0 -45
- package/src/notifications/registerBroadcastAuth.test.ts +0 -134
- package/src/notifications/registerBroadcastAuth.ts +0 -100
- package/src/notifications/resolveSavedNotification.test.ts +0 -82
- package/src/notifications/resolveSavedNotification.ts +0 -59
- package/src/notifications/types.ts +0 -93
- package/src/orm/m2mAccessor.ts +0 -66
- package/src/orm/modelDefaults.test.ts +0 -633
- package/src/orm/modelDefaults.ts +0 -666
- package/src/pageData/breadcrumbs.ts +0 -288
- package/src/pageData/forms.ts +0 -578
- package/src/pageData/helpers.ts +0 -857
- package/src/pageData/misc.ts +0 -347
- package/src/pageData/navigation.ts +0 -842
- package/src/pageData/relationPages.ts +0 -1248
- package/src/pageData/relationTabs.ts +0 -286
- package/src/pageData/resourcePages.ts +0 -609
- package/src/pageData.test.ts +0 -1545
- package/src/pageData.ts +0 -341
- package/src/plugins/index.ts +0 -8
- package/src/plugins/themeEditor.test.ts +0 -36
- package/src/plugins/themeEditor.ts +0 -45
- package/src/react/AppShell.tsx +0 -251
- package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
- package/src/react/CollabRoomContext.ts +0 -98
- package/src/react/CollabTextRendererRegistry.ts +0 -102
- package/src/react/CommandPalette.tsx +0 -375
- package/src/react/CurrentUserContext.tsx +0 -50
- package/src/react/CustomPageWrapperGate.tsx +0 -69
- package/src/react/CustomPageWrapperRegistry.ts +0 -45
- package/src/react/FieldFocusReporterRegistry.ts +0 -37
- package/src/react/FieldLabelSlotRegistry.ts +0 -30
- package/src/react/FieldPresenceRegistry.ts +0 -46
- package/src/react/FormCollabBindingRegistry.ts +0 -242
- package/src/react/FormStateContext.tsx +0 -591
- package/src/react/HeadHooks.tsx +0 -126
- package/src/react/MarkdownEditorRegistry.test.ts +0 -38
- package/src/react/MarkdownEditorRegistry.ts +0 -107
- package/src/react/NotificationActionStrip.tsx +0 -263
- package/src/react/NotificationBell.tsx +0 -426
- package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
- package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
- package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
- package/src/react/PendingSuggestionsContext.tsx +0 -172
- package/src/react/RecordWrapperGate.tsx +0 -58
- package/src/react/RecordWrapperRegistry.ts +0 -39
- package/src/react/RenderHookSlot.tsx +0 -32
- package/src/react/RightSidebar.tsx +0 -257
- package/src/react/RightSidebarContext.tsx +0 -234
- package/src/react/RightSidebarTrigger.tsx +0 -53
- package/src/react/RowCoordsContext.tsx +0 -23
- package/src/react/SchemaRenderer.tsx +0 -549
- package/src/react/SearchTrigger.tsx +0 -46
- package/src/react/ThemeProvider.tsx +0 -93
- package/src/react/ThemeSettingsPage.tsx +0 -579
- package/src/react/ThemeToggle.tsx +0 -20
- package/src/react/Toaster.tsx +0 -158
- package/src/react/UserMenu.tsx +0 -196
- package/src/react/WidgetDataContext.tsx +0 -157
- package/src/react/cells/EditableCell.tsx +0 -389
- package/src/react/component-slots.test.ts +0 -103
- package/src/react/component-slots.ts +0 -116
- package/src/react/fieldJsHandler.test.ts +0 -166
- package/src/react/fieldJsHandler.ts +0 -79
- package/src/react/fields/BuilderInput.tsx +0 -1078
- package/src/react/fields/CheckboxInput.tsx +0 -39
- package/src/react/fields/CheckboxListInput.tsx +0 -102
- package/src/react/fields/ColorInput.tsx +0 -71
- package/src/react/fields/DateFieldInput.tsx +0 -70
- package/src/react/fields/DateTimeInput.tsx +0 -62
- package/src/react/fields/FieldShell.tsx +0 -348
- package/src/react/fields/FileUploadInput.tsx +0 -639
- package/src/react/fields/HiddenInput.tsx +0 -17
- package/src/react/fields/KeyValueInput.tsx +0 -230
- package/src/react/fields/MarkdownInput.tsx +0 -560
- package/src/react/fields/RadioInput.tsx +0 -81
- package/src/react/fields/RepeaterInput.test.ts +0 -116
- package/src/react/fields/RepeaterInput.tsx +0 -1420
- package/src/react/fields/SelectFieldInput.tsx +0 -280
- package/src/react/fields/SliderInput.tsx +0 -81
- package/src/react/fields/TagsInput.tsx +0 -283
- package/src/react/fields/TextLikeInput.tsx +0 -256
- package/src/react/fields/ToggleButtonsInput.tsx +0 -60
- package/src/react/fields/ToggleFieldInput.tsx +0 -56
- package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
- package/src/react/fields/relationshipRenameDispatch.ts +0 -97
- package/src/react/fields/repeaterReconcile.test.ts +0 -114
- package/src/react/fields/repeaterReconcile.ts +0 -104
- package/src/react/fields/rowChromeButton.tsx +0 -336
- package/src/react/fields/rowState.ts +0 -106
- package/src/react/fields/syncRowGates.test.ts +0 -202
- package/src/react/fields/syncRowGates.ts +0 -66
- package/src/react/fields/textInputControls.tsx +0 -238
- package/src/react/fields/useRowReorderDnd.ts +0 -78
- package/src/react/formStateHelpers.test.ts +0 -508
- package/src/react/formStateHelpers.ts +0 -381
- package/src/react/hooks/use-mobile.ts +0 -19
- package/src/react/icon-context.tsx +0 -60
- package/src/react/index.ts +0 -195
- package/src/react/layouts/SidebarLayout.tsx +0 -250
- package/src/react/layouts/TopbarLayout.tsx +0 -258
- package/src/react/navigate.tsx +0 -37
- package/src/react/onProviderSynced.test.ts +0 -90
- package/src/react/parseRecordEditUrl.test.ts +0 -122
- package/src/react/parseRecordEditUrl.ts +0 -94
- package/src/react/persistedState.ts +0 -40
- package/src/react/registry.ts +0 -48
- package/src/react/right-panel-registry.tsx +0 -47
- package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
- package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
- package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
- package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
- package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
- package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
- package/src/react/schemaRenderer/action/buttons.tsx +0 -99
- package/src/react/schemaRenderer/action/helpers.ts +0 -140
- package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
- package/src/react/schemaRenderer/columnFormat.ts +0 -65
- package/src/react/schemaRenderer/constants.ts +0 -50
- package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
- package/src/react/schemaRenderer/form/renderField.tsx +0 -511
- package/src/react/schemaRenderer/helpers.tsx +0 -81
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
- package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
- package/src/react/schemaRenderer/table/filters.tsx +0 -1233
- package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
- package/src/react/schemaRenderer/table/links.tsx +0 -112
- package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
- package/src/react/schemaRenderer/table/url.tsx +0 -143
- package/src/react/theme-preview/apply.ts +0 -99
- package/src/react/theme-preview/build-html.ts +0 -436
- package/src/react/ui/button.tsx +0 -51
- package/src/react/ui/calendar.tsx +0 -67
- package/src/react/ui/checkbox.tsx +0 -29
- package/src/react/ui/dialog.tsx +0 -108
- package/src/react/ui/dropdown-menu.tsx +0 -97
- package/src/react/ui/input.tsx +0 -20
- package/src/react/ui/label.tsx +0 -21
- package/src/react/ui/popover.tsx +0 -50
- package/src/react/ui/select.tsx +0 -169
- package/src/react/ui/separator.tsx +0 -25
- package/src/react/ui/sheet.tsx +0 -136
- package/src/react/ui/sidebar.tsx +0 -723
- package/src/react/ui/skeleton.tsx +0 -13
- package/src/react/ui/slider.tsx +0 -34
- package/src/react/ui/switch.tsx +0 -28
- package/src/react/ui/table.tsx +0 -105
- package/src/react/ui/tabs.tsx +0 -63
- package/src/react/ui/textarea.tsx +0 -18
- package/src/react/ui/tooltip.tsx +0 -64
- package/src/react/useCollabSeed.ts +0 -86
- package/src/react/useResizableWidth.ts +0 -139
- package/src/react/utils.ts +0 -6
- package/src/react/widgetRegistry.test.ts +0 -43
- package/src/react/widgetRegistry.ts +0 -50
- package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
- package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
- package/src/react/widgets/ViewRenderer.tsx +0 -71
- package/src/relationManagerData.test.ts +0 -1595
- package/src/richtext/index.ts +0 -8
- package/src/richtext/registry.ts +0 -89
- package/src/routes/globals.ts +0 -148
- package/src/routes/guard.test.ts +0 -325
- package/src/routes/helpers.ts +0 -700
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1227
- package/src/routes/resources.ts +0 -781
- package/src/routes/theme.ts +0 -91
- package/src/routes-nested-relations.test.ts +0 -676
- package/src/routes-relations.test.ts +0 -972
- package/src/routes.test.ts +0 -2027
- package/src/routes.ts +0 -303
- package/src/schema/Alert.test.ts +0 -109
- package/src/schema/Alert.ts +0 -131
- package/src/schema/Block.ts +0 -169
- package/src/schema/Breadcrumbs.ts +0 -40
- package/src/schema/Card.ts +0 -35
- package/src/schema/Divider.ts +0 -20
- package/src/schema/Element.ts +0 -219
- package/src/schema/EmptyState.test.ts +0 -37
- package/src/schema/EmptyState.ts +0 -63
- package/src/schema/Fieldset.ts +0 -43
- package/src/schema/Grid.ts +0 -43
- package/src/schema/Group.ts +0 -30
- package/src/schema/Heading.ts +0 -39
- package/src/schema/Html.ts +0 -67
- package/src/schema/Icon.ts +0 -54
- package/src/schema/Image.ts +0 -57
- package/src/schema/LinkTag.ts +0 -41
- package/src/schema/Markdown.ts +0 -85
- package/src/schema/MetaTag.ts +0 -41
- package/src/schema/RelationTabs.ts +0 -71
- package/src/schema/ScriptTag.ts +0 -55
- package/src/schema/Section.ts +0 -160
- package/src/schema/ServerDataElement.test.ts +0 -140
- package/src/schema/ServerDataElement.ts +0 -156
- package/src/schema/SlotComponent.test.ts +0 -77
- package/src/schema/SlotComponent.ts +0 -71
- package/src/schema/Split.ts +0 -50
- package/src/schema/Stat.test.ts +0 -118
- package/src/schema/Stat.ts +0 -154
- package/src/schema/StatsOverview.test.ts +0 -141
- package/src/schema/StatsOverview.ts +0 -119
- package/src/schema/StyleTag.ts +0 -35
- package/src/schema/TableWidget.test.ts +0 -297
- package/src/schema/TableWidget.ts +0 -289
- package/src/schema/Tabs.ts +0 -79
- package/src/schema/Text.ts +0 -58
- package/src/schema/UnorderedList.ts +0 -49
- package/src/schema/View.test.ts +0 -111
- package/src/schema/View.ts +0 -127
- package/src/schema/Wizard.ts +0 -220
- package/src/schema/containers.test.ts +0 -564
- package/src/schema/headTags.test.ts +0 -134
- package/src/schema/index.ts +0 -40
- package/src/schema/primes.test.ts +0 -269
- package/src/schema/resolveSchema.test.ts +0 -379
- package/src/schema/resolveSchema.ts +0 -917
- package/src/schema/sanitize.ts +0 -58
- package/src/search.test.ts +0 -446
- package/src/search.ts +0 -178
- package/src/sessionFilters.test.ts +0 -375
- package/src/sessionFilters.ts +0 -143
- package/src/slot-components/index.ts +0 -10
- package/src/slot-components/registry.ts +0 -56
- package/src/styles/file-upload.css +0 -13
- package/src/summarizers/Summarizer.test.ts +0 -84
- package/src/summarizers/Summarizer.ts +0 -123
- package/src/summarizers/index.ts +0 -11
- package/src/theme/base-colors.ts +0 -68
- package/src/theme/chart-colors.ts +0 -50
- package/src/theme/colors.ts +0 -447
- package/src/theme/generate-css.test.ts +0 -139
- package/src/theme/generate-css.ts +0 -44
- package/src/theme/generate-scale.test.ts +0 -106
- package/src/theme/generate-scale.ts +0 -97
- package/src/theme/icon-map.ts +0 -42
- package/src/theme/index.ts +0 -34
- package/src/theme/migrate.test.ts +0 -178
- package/src/theme/migrate.ts +0 -81
- package/src/theme/presets.ts +0 -135
- package/src/theme/radius.ts +0 -18
- package/src/theme/resolve.test.ts +0 -238
- package/src/theme/resolve.ts +0 -96
- package/src/theme/spacing.ts +0 -18
- package/src/theme/storage.test.ts +0 -126
- package/src/theme/storage.ts +0 -106
- package/src/theme/theme-colors.ts +0 -88
- package/src/theme/types.ts +0 -125
- package/src/uploads/UploadAdapter.ts +0 -35
- package/src/uploads/index.ts +0 -2
- package/src/uploads/localUpload.test.ts +0 -70
- package/src/uploads/localUpload.ts +0 -84
- package/src/validation/Validator.ts +0 -49
- package/src/validation/index.ts +0 -28
- package/src/validation/rules.ts +0 -78
- package/src/validation/runValidators.ts +0 -435
- package/src/validation/uniqueValidator.test.ts +0 -196
- package/src/validation/uniqueValidator.ts +0 -133
- package/src/validation/validators.test.ts +0 -268
- package/src/vite.test.ts +0 -184
- package/src/vite.ts +0 -787
- package/src/widgets/index.ts +0 -10
- package/src/widgets/registry.ts +0 -45
- package/src/widgets.test.ts +0 -592
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -10
- package/views/react/Dashboard.tsx +0 -27
- package/views/react/Resources/Form.tsx +0 -102
- package/views/react/Resources/Index.tsx +0 -49
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internals for the `Action.import` factory. Lives in its own module
|
|
3
|
-
* to keep the cycle between `Action.ts` and the field types (Select /
|
|
4
|
-
* FileUpload) honest — `Action.import` calls in here via dynamic import
|
|
5
|
-
* inside the handler.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ActionContext } from './Action.js'
|
|
9
|
-
import { parseCsv } from '../io/csv.js'
|
|
10
|
-
import type { Element } from '../schema/Element.js'
|
|
11
|
-
import { FileUpload } from '../fields/FileUploadField.js'
|
|
12
|
-
import { SelectField } from '../fields/SelectField.js'
|
|
13
|
-
|
|
14
|
-
export type ImportFormat = 'csv' | 'json'
|
|
15
|
-
|
|
16
|
-
export interface ImportSummary {
|
|
17
|
-
created: number
|
|
18
|
-
updated: number
|
|
19
|
-
skipped: number
|
|
20
|
-
/** Per-row failure messages (non-empty `validate` returns + thrown
|
|
21
|
-
* errors during create / update). 1-indexed for human-friendly
|
|
22
|
-
* display in the success notification. */
|
|
23
|
-
errors: Array<{ row: number; message: string }>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ImportContext extends ActionContext {
|
|
27
|
-
rowIndex?: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ImportOptions {
|
|
31
|
-
/** CSV / JSON header → model attribute key. Default: identity (the
|
|
32
|
-
* CSV header IS the model attribute). For JSON, this remaps the
|
|
33
|
-
* parsed object keys 1:1. */
|
|
34
|
-
columns?: Record<string, string>
|
|
35
|
-
/** Output format. Default `'csv'`. JSON-format imports parse the
|
|
36
|
-
* uploaded file as `JSON.parse(text)`; arrays import as-is, single
|
|
37
|
-
* objects wrap into `[obj]`. */
|
|
38
|
-
format?: ImportFormat
|
|
39
|
-
/** Model attribute used as the upsert key. When set, the importer
|
|
40
|
-
* looks up an existing record by `M.query().where(upsertBy, row[upsertBy])`
|
|
41
|
-
* before deciding create vs update. When omitted, every row is a
|
|
42
|
-
* create. */
|
|
43
|
-
upsertBy?: string
|
|
44
|
-
/** Per-row guard. Return a string to skip the row + accumulate as a
|
|
45
|
-
* failure; return `null` (or `undefined`) to allow the row through.
|
|
46
|
-
* Async-aware. */
|
|
47
|
-
validate?: (
|
|
48
|
-
row: Record<string, unknown>,
|
|
49
|
-
ctx: ImportContext,
|
|
50
|
-
) => string | null | undefined | Promise<string | null | undefined>
|
|
51
|
-
/** Mutate the row payload before `M.create`. Async-aware. */
|
|
52
|
-
beforeCreate?: (
|
|
53
|
-
row: Record<string, unknown>,
|
|
54
|
-
ctx: ImportContext,
|
|
55
|
-
) => Record<string, unknown> | Promise<Record<string, unknown>>
|
|
56
|
-
/** Mutate the row payload before `M.update`. Receives the existing
|
|
57
|
-
* record so the hook can copy / preserve fields the import shouldn't
|
|
58
|
-
* overwrite. Async-aware. */
|
|
59
|
-
beforeUpdate?: (
|
|
60
|
-
row: Record<string, unknown>,
|
|
61
|
-
existing: unknown,
|
|
62
|
-
ctx: ImportContext,
|
|
63
|
-
) => Record<string, unknown> | Promise<Record<string, unknown>>
|
|
64
|
-
/** Hard cap on the row count. Aborts with an error notification when
|
|
65
|
-
* exceeded — protects against accidental million-row uploads. Default
|
|
66
|
-
* `10_000`. */
|
|
67
|
-
maxRows?: number
|
|
68
|
-
/** Number of rows to process in parallel. The importer chunks the row
|
|
69
|
-
* list into batches of this size and runs each chunk via `Promise.all`.
|
|
70
|
-
* Order within a chunk is non-deterministic; row indices in error
|
|
71
|
-
* messages still match the original CSV/JSON position. Default `10`. */
|
|
72
|
-
concurrency?: number
|
|
73
|
-
/** Final hook after the import loop. Useful for audit-log writes,
|
|
74
|
-
* cache invalidation, etc. Async-aware. */
|
|
75
|
-
onComplete?: (summary: ImportSummary, ctx: ImportContext) => void | Promise<void>
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Parse an uploaded file's text into an array of row objects keyed by
|
|
80
|
-
* model attribute (after applying `opts.columns` remap for CSV).
|
|
81
|
-
*
|
|
82
|
-
* Throws on invalid CSV / JSON; the caller's outer `try` surfaces the
|
|
83
|
-
* error message via the result notification.
|
|
84
|
-
*/
|
|
85
|
-
export function parseImportText(
|
|
86
|
-
text: string,
|
|
87
|
-
format: ImportFormat,
|
|
88
|
-
columns?: Record<string, string>,
|
|
89
|
-
): Array<Record<string, unknown>> {
|
|
90
|
-
if (format === 'json') {
|
|
91
|
-
const parsed = JSON.parse(text) as unknown
|
|
92
|
-
if (Array.isArray(parsed)) return parsed as Array<Record<string, unknown>>
|
|
93
|
-
if (parsed && typeof parsed === 'object') return [parsed as Record<string, unknown>]
|
|
94
|
-
throw new Error('JSON import expected an array or object')
|
|
95
|
-
}
|
|
96
|
-
const csv = parseCsv(text)
|
|
97
|
-
if (!columns || Object.keys(columns).length === 0) {
|
|
98
|
-
return csv.rows
|
|
99
|
-
}
|
|
100
|
-
// Remap CSV headers → model keys. CSV cells stay as strings; the ORM
|
|
101
|
-
// does any final coercion (or the user's `beforeCreate` hook).
|
|
102
|
-
return csv.rows.map(row => {
|
|
103
|
-
const out: Record<string, unknown> = {}
|
|
104
|
-
for (const [csvKey, value] of Object.entries(row)) {
|
|
105
|
-
const modelKey = columns[csvKey] ?? csvKey
|
|
106
|
-
out[modelKey] = value
|
|
107
|
-
}
|
|
108
|
-
return out
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Run the per-row import loop. Pulled out of the factory body so the
|
|
114
|
-
* factory stays at the level of "compose modal + handler" instead of
|
|
115
|
-
* "do the work".
|
|
116
|
-
*
|
|
117
|
-
* Returns the aggregated summary (counts + per-row error messages).
|
|
118
|
-
* Failures don't roll back — partial-success is the contract. The
|
|
119
|
-
* caller surfaces the summary via a notification.
|
|
120
|
-
*/
|
|
121
|
-
export async function runImport(
|
|
122
|
-
rows: Array<Record<string, unknown>>,
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
-
M: any,
|
|
125
|
-
mode: 'create' | 'upsert',
|
|
126
|
-
opts: ImportOptions,
|
|
127
|
-
ctx: ImportContext,
|
|
128
|
-
): Promise<ImportSummary> {
|
|
129
|
-
const summary: ImportSummary = { created: 0, updated: 0, skipped: 0, errors: [] }
|
|
130
|
-
const upsertBy = opts.upsertBy
|
|
131
|
-
const concurrency = Math.max(1, opts.concurrency ?? 10)
|
|
132
|
-
|
|
133
|
-
// Per-row outcome — a small value type so chunks can resolve in any
|
|
134
|
-
// order and we still accumulate `summary` in original-row order.
|
|
135
|
-
type RowOutcome =
|
|
136
|
-
| { kind: 'created' }
|
|
137
|
-
| { kind: 'updated' }
|
|
138
|
-
| { kind: 'skipped'; row: number; message: string }
|
|
139
|
-
|
|
140
|
-
async function processRow(row: Record<string, unknown>, i: number): Promise<RowOutcome> {
|
|
141
|
-
const rowCtx: ImportContext = { ...ctx, rowIndex: i }
|
|
142
|
-
try {
|
|
143
|
-
const guard = await opts.validate?.(row, rowCtx)
|
|
144
|
-
if (typeof guard === 'string' && guard.length > 0) {
|
|
145
|
-
return { kind: 'skipped', row: i + 1, message: guard }
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (mode === 'upsert' && upsertBy) {
|
|
149
|
-
const slice = await M.query().where(upsertBy, row[upsertBy]).paginate(1, 1)
|
|
150
|
-
const existing = (slice?.data ?? [])[0]
|
|
151
|
-
if (existing) {
|
|
152
|
-
const id = String((existing as { id?: unknown }).id ?? '')
|
|
153
|
-
const payload = opts.beforeUpdate
|
|
154
|
-
? await opts.beforeUpdate(row, existing, rowCtx)
|
|
155
|
-
: row
|
|
156
|
-
await M.update(id, payload)
|
|
157
|
-
return { kind: 'updated' }
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const payload = opts.beforeCreate
|
|
162
|
-
? await opts.beforeCreate(row, rowCtx)
|
|
163
|
-
: row
|
|
164
|
-
await M.create(payload)
|
|
165
|
-
return { kind: 'created' }
|
|
166
|
-
} catch (err) {
|
|
167
|
-
return { kind: 'skipped', row: i + 1, message: err instanceof Error ? err.message : 'unknown' }
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
for (let start = 0; start < rows.length; start += concurrency) {
|
|
172
|
-
const slice = rows.slice(start, start + concurrency)
|
|
173
|
-
const outcomes = await Promise.all(slice.map((row, idx) => processRow(row, start + idx)))
|
|
174
|
-
for (const outcome of outcomes) {
|
|
175
|
-
if (outcome.kind === 'created') summary.created++
|
|
176
|
-
else if (outcome.kind === 'updated') summary.updated++
|
|
177
|
-
else {
|
|
178
|
-
summary.skipped++
|
|
179
|
-
summary.errors.push({ row: outcome.row, message: outcome.message })
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
await opts.onComplete?.(summary, ctx)
|
|
185
|
-
return summary
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** Build the modal-form schema for an import action. v1 ships:
|
|
189
|
-
*
|
|
190
|
-
* - `FileUpload.make('file')` — required, capped at 10 MB, accepts
|
|
191
|
-
* `.csv,.json` by default; users can override the field via the
|
|
192
|
-
* `Action.schema([...])` chain.
|
|
193
|
-
* - When `upsertBy` is set, a `Select.make('mode')` toggles between
|
|
194
|
-
* "Create only" and "Create or update" with `'upsert'` as the
|
|
195
|
-
* default selection.
|
|
196
|
-
*
|
|
197
|
-
* Returns an `Element[]` the factory drops into `Action.schema(...)`
|
|
198
|
-
* to drive the auto-built modal form. */
|
|
199
|
-
export function buildImportSchema(opts: ImportOptions): Element[] {
|
|
200
|
-
const elements: Element[] = []
|
|
201
|
-
|
|
202
|
-
const file = FileUpload.make('file')
|
|
203
|
-
.label('File')
|
|
204
|
-
.accept(['.csv', '.json', 'text/csv', 'application/json'])
|
|
205
|
-
.maxSize(10_000_000)
|
|
206
|
-
.required()
|
|
207
|
-
elements.push(file as unknown as Element)
|
|
208
|
-
|
|
209
|
-
if (typeof opts.upsertBy === 'string' && opts.upsertBy.length > 0) {
|
|
210
|
-
const mode = SelectField.make('mode')
|
|
211
|
-
.label('Mode')
|
|
212
|
-
.options([
|
|
213
|
-
{ value: 'create', label: 'Create only' },
|
|
214
|
-
{ value: 'upsert', label: 'Create or update' },
|
|
215
|
-
])
|
|
216
|
-
.default('upsert')
|
|
217
|
-
elements.push(mode as unknown as Element)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return elements
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** Build a one-line success / warning notification body for the
|
|
224
|
-
* import summary — surfaces the first 5 row failures inline so users
|
|
225
|
-
* see what went wrong without hunting through logs. */
|
|
226
|
-
export function buildImportNotification(summary: ImportSummary, opts: { upsert: boolean }): {
|
|
227
|
-
type: 'success' | 'warning' | 'error'
|
|
228
|
-
title: string
|
|
229
|
-
body?: string
|
|
230
|
-
} {
|
|
231
|
-
const total = summary.created + summary.updated + summary.skipped
|
|
232
|
-
if (total === 0) {
|
|
233
|
-
return { type: 'warning', title: 'Import complete — no rows processed' }
|
|
234
|
-
}
|
|
235
|
-
const parts: string[] = [`${summary.created} created`]
|
|
236
|
-
if (opts.upsert) parts.push(`${summary.updated} updated`)
|
|
237
|
-
if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`)
|
|
238
|
-
const title = `Import complete — ${parts.join(', ')}`
|
|
239
|
-
const type: 'success' | 'warning' = summary.errors.length > 0 ? 'warning' : 'success'
|
|
240
|
-
if (summary.errors.length === 0) return { type, title }
|
|
241
|
-
const body = summary.errors.slice(0, 5).map(e => `Row ${e.row}: ${e.message}`).join('\n')
|
|
242
|
-
return { type, title, body }
|
|
243
|
-
}
|
package/src/actions/index.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
Action,
|
|
3
|
-
type ActionPlacement,
|
|
4
|
-
type ActionContext,
|
|
5
|
-
type ActionHandler,
|
|
6
|
-
type ActionConfirm,
|
|
7
|
-
type ActionMeta,
|
|
8
|
-
type ActionColor,
|
|
9
|
-
type ActionSize,
|
|
10
|
-
type ActionVisibilityContext,
|
|
11
|
-
type VisibilityRule,
|
|
12
|
-
} from './Action.js'
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
ActionGroup,
|
|
16
|
-
type ActionGroupMeta,
|
|
17
|
-
} from './ActionGroup.js'
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* M2M relation Action factories — `relationAttach` (header, modal-form
|
|
3
|
-
* picker → POST `_action/relationAttach`), `relationDetach` (row, direct
|
|
4
|
-
* POST to `_detach/:childId`), `relationBulkDetach` (bulk, handler-
|
|
5
|
-
* dispatched).
|
|
6
|
-
*
|
|
7
|
-
* Sibling of `relationCreate / Edit / Delete` for every M2M mode
|
|
8
|
-
* (`belongsToMany`, `morphToMany` (owning polymorphic side),
|
|
9
|
-
* `morphedByMany` (inverse polymorphic side)). All three modes share
|
|
10
|
-
* the same `attach` / `detach` / `sync` accessor surface — the rudder
|
|
11
|
-
* ORM stamps + filters the polymorphic discriminator on the morph
|
|
12
|
-
* variants automatically, so pilotiq's pivot factories are mode-agnostic
|
|
13
|
-
* beyond the visibility gate.
|
|
14
|
-
*
|
|
15
|
-
* All three auto-hide outside any M2M mode so dropping a factory into
|
|
16
|
-
* a non-M2M manager is a no-op (visible=false) instead of a confusing
|
|
17
|
-
* 404.
|
|
18
|
-
*
|
|
19
|
-
* The first and third route through the manager-scoped
|
|
20
|
-
* `_action/:actionName` endpoint (added in routes.ts) so handlers
|
|
21
|
-
* see `ctx.relation = { parent, parentId, relationship }`.
|
|
22
|
-
*
|
|
23
|
-
* See `docs/plans/action-split.md` for the split plan.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { Action, type ActionResult } from './Action.js'
|
|
27
|
-
import {
|
|
28
|
-
safeManagerPolicy,
|
|
29
|
-
type RelationManager,
|
|
30
|
-
type RelationManagerContext,
|
|
31
|
-
} from '../RelationManager.js'
|
|
32
|
-
import { resolveM2MAccessor } from '../orm/m2mAccessor.js'
|
|
33
|
-
import { buildAttachModalSchema } from './attachFactory.js'
|
|
34
|
-
import { isM2MMode, relationUrlPrefix } from './factoryHelpers.js'
|
|
35
|
-
|
|
36
|
-
/** Resolve the M2M accessor on `rel.parent`, null-check the requested
|
|
37
|
-
* method, run it, and shape the per-failure-mode error envelope.
|
|
38
|
-
* Used by `relationAttachAction` and `relationBulkDetachAction` —
|
|
39
|
-
* both follow the same pattern (resolve → null-check method →
|
|
40
|
-
* try/catch). Keeps the "Pivot accessor missing on …" error string
|
|
41
|
-
* consistent across both call sites. */
|
|
42
|
-
async function callM2MAccessor(
|
|
43
|
-
rel: { parent: unknown; relationship: string },
|
|
44
|
-
method: 'attach' | 'detach',
|
|
45
|
-
ids: string[],
|
|
46
|
-
failureLabel: string,
|
|
47
|
-
): Promise<{ ok: true } | { ok: false; result: ActionResult }> {
|
|
48
|
-
const accessor = resolveM2MAccessor(rel.parent, rel.relationship) as
|
|
49
|
-
| { attach?: (ids: string[]) => Promise<unknown>; detach?: (ids: string[]) => Promise<unknown> }
|
|
50
|
-
| null
|
|
51
|
-
const fn = accessor?.[method]
|
|
52
|
-
if (typeof fn !== 'function') {
|
|
53
|
-
return {
|
|
54
|
-
ok: false,
|
|
55
|
-
result: { notify: { title: `Pivot accessor missing on ${rel.relationship} — wrong relation type or ORM version?`, type: 'error' } as never },
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
await fn(ids)
|
|
60
|
-
return { ok: true }
|
|
61
|
-
} catch (err) {
|
|
62
|
-
return {
|
|
63
|
-
ok: false,
|
|
64
|
-
result: { notify: { title: `${failureLabel}: ${err instanceof Error ? err.message : String(err)}`, type: 'error' } as never },
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Header-placement attach factory — opens a modal with a SelectField
|
|
70
|
-
* listing related records that aren't already attached, and POSTs the
|
|
71
|
-
* selected id to the manager's `_action/relationAttach` endpoint.
|
|
72
|
-
*
|
|
73
|
-
* Visibility delegates to `M.canAttach(user, parentRecord)` AND
|
|
74
|
-
* guards against being dropped into a non-M2M manager. */
|
|
75
|
-
export function relationAttachAction(
|
|
76
|
-
M: typeof RelationManager,
|
|
77
|
-
ctx: RelationManagerContext,
|
|
78
|
-
): Action {
|
|
79
|
-
const labelSingular = M.getLabelSingular()
|
|
80
|
-
const a = Action.make('relationAttach')
|
|
81
|
-
.label(`Attach ${labelSingular}`)
|
|
82
|
-
.header()
|
|
83
|
-
.modalHeading(`Attach ${labelSingular}`)
|
|
84
|
-
.modalSubmitLabel('Attach')
|
|
85
|
-
.modalCancelLabel('Cancel')
|
|
86
|
-
.handler(async (hctx) => {
|
|
87
|
-
const rel = hctx.relation
|
|
88
|
-
if (!rel) {
|
|
89
|
-
return { notify: { title: 'Attach handler missing parent context — manager-scoped _action route not wired', type: 'error' } as never }
|
|
90
|
-
}
|
|
91
|
-
const Related = ctx.related
|
|
92
|
-
if (!Related?.model) {
|
|
93
|
-
return { notify: { title: 'Cannot attach: related Resource has no model', type: 'error' } as never }
|
|
94
|
-
}
|
|
95
|
-
const idStr = String((hctx.values?.['_attachId'] as unknown) ?? '')
|
|
96
|
-
if (idStr.length === 0) {
|
|
97
|
-
return { notify: { title: 'Pick a record to attach', type: 'error' } as never }
|
|
98
|
-
}
|
|
99
|
-
const call = await callM2MAccessor(rel, 'attach', [idStr], 'Attach failed')
|
|
100
|
-
if (!call.ok) return call.result
|
|
101
|
-
return { notify: { title: `${labelSingular} attached`, type: 'success' } as never }
|
|
102
|
-
})
|
|
103
|
-
.visible(({ user }) => {
|
|
104
|
-
if (!isM2MMode(ctx.mode)) return false
|
|
105
|
-
return safeManagerPolicy(M, 'canAttach', ctx.related, user, ctx.parentRecord)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// Build the modal-form schema only when this is actually an M2M
|
|
109
|
-
// manager — non-M2M drops keep the action hidden via the visibility
|
|
110
|
-
// predicate, but still need a schema-less Action so the meta walker
|
|
111
|
-
// doesn't blow up. Static import is fine: `attachFactory` only
|
|
112
|
-
// depends on `SelectField` + ORM helpers, no cycle back to Action.
|
|
113
|
-
if (isM2MMode(ctx.mode) && ctx.related?.model) {
|
|
114
|
-
a.schema(buildAttachModalSchema({
|
|
115
|
-
Related: ctx.related,
|
|
116
|
-
relationship: ctx.relationship,
|
|
117
|
-
recordTitleAttr: M.getRecordTitleAttribute() ?? ctx.related.recordTitleAttribute,
|
|
118
|
-
labelSingular,
|
|
119
|
-
}))
|
|
120
|
-
}
|
|
121
|
-
return a
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Row-placement detach factory — POSTs to
|
|
125
|
-
* `${base}/${parentSlug}/${parentId}/${relationship}/${recordId ?? ':id'}/_detach`,
|
|
126
|
-
* destructive style with a confirmation prompt that says "Detach"
|
|
127
|
-
* (not "Delete") so users understand the target record stays.
|
|
128
|
-
* Visibility delegates to `M.canDetach`. */
|
|
129
|
-
export function relationDetachAction(
|
|
130
|
-
M: typeof RelationManager,
|
|
131
|
-
ctx: RelationManagerContext,
|
|
132
|
-
recordId?: string,
|
|
133
|
-
): Action {
|
|
134
|
-
const id = recordId ?? ':id'
|
|
135
|
-
const singular = M.getLabelSingular().toLowerCase()
|
|
136
|
-
return Action.make('relationDetach')
|
|
137
|
-
.label('Detach')
|
|
138
|
-
.destructive()
|
|
139
|
-
.method('post')
|
|
140
|
-
.action(`${relationUrlPrefix(ctx)}/${id}/_detach`)
|
|
141
|
-
.confirm(`Detach this ${singular}? The ${singular} record stays in place; only the link is removed.`)
|
|
142
|
-
.visible(async ({ user, record }) => {
|
|
143
|
-
if (!isM2MMode(ctx.mode)) return false
|
|
144
|
-
return safeManagerPolicy(M, 'canDetach', ctx.related, user, ctx.parentRecord, record)
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Bulk-placement bulk-detach factory — handler-dispatched. Calls
|
|
149
|
-
* `parent.related(rel).detach(ids)` for the selected rows. Visibility
|
|
150
|
-
* delegates to `M.canAttach` (acts like a "manager admin" gate; we
|
|
151
|
-
* intentionally don't enforce per-row `canDetach` on the visibility
|
|
152
|
-
* side because the bulk button needs to be visible before the user
|
|
153
|
-
* has selected anything — per-row gating happens inside the handler). */
|
|
154
|
-
export function relationBulkDetachAction(
|
|
155
|
-
M: typeof RelationManager,
|
|
156
|
-
ctx: RelationManagerContext,
|
|
157
|
-
): Action {
|
|
158
|
-
const labelPlural = M.getLabel().toLowerCase()
|
|
159
|
-
return Action.make('relationBulkDetach')
|
|
160
|
-
.label('Detach selected')
|
|
161
|
-
.destructive()
|
|
162
|
-
.bulk()
|
|
163
|
-
.confirm(`Detach the selected ${labelPlural}? The records stay in place; only the links are removed.`)
|
|
164
|
-
.handler(async (hctx) => {
|
|
165
|
-
const rel = hctx.relation
|
|
166
|
-
if (!rel) {
|
|
167
|
-
return { notify: { title: 'Bulk-detach handler missing parent context — manager-scoped _action route not wired', type: 'error' } as never }
|
|
168
|
-
}
|
|
169
|
-
const records = hctx.records ?? []
|
|
170
|
-
// Parallelize the per-row policy probes; the accessor call itself stays a single batched op.
|
|
171
|
-
const allowedFlags = await Promise.all(
|
|
172
|
-
records.map(r => safeManagerPolicy(M, 'canDetach', ctx.related, hctx.user, ctx.parentRecord, r)),
|
|
173
|
-
)
|
|
174
|
-
const ids: string[] = []
|
|
175
|
-
for (let i = 0; i < records.length; i++) {
|
|
176
|
-
if (!allowedFlags[i]) continue
|
|
177
|
-
const id = String((records[i] as { id?: unknown }).id ?? '')
|
|
178
|
-
if (id) ids.push(id)
|
|
179
|
-
}
|
|
180
|
-
if (ids.length === 0) {
|
|
181
|
-
return { notify: { title: 'Nothing to detach (no permitted rows)', type: 'warning' } as never }
|
|
182
|
-
}
|
|
183
|
-
const call = await callM2MAccessor(rel, 'detach', ids, 'Bulk detach failed')
|
|
184
|
-
if (!call.ok) return call.result
|
|
185
|
-
return { notify: { title: `${ids.length} ${labelPlural} detached`, type: 'success' } as never }
|
|
186
|
-
})
|
|
187
|
-
.visible(({ user }) => {
|
|
188
|
-
if (!isM2MMode(ctx.mode)) return false
|
|
189
|
-
// Bulk gate uses canAttach as a stand-in for "manager admin" —
|
|
190
|
-
// per-row canDetach is enforced inside the handler.
|
|
191
|
-
return safeManagerPolicy(M, 'canAttach', ctx.related, user, ctx.parentRecord)
|
|
192
|
-
})
|
|
193
|
-
}
|