@pilotiq/pilotiq 0.24.1 → 0.24.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 +57 -0
- package/boost/guidelines.md +571 -0
- package/boost/skills/pilotiq-actions/SKILL.md +49 -0
- package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
- package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
- package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -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/Pilotiq.d.ts +31 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +3 -1
- package/dist/Pilotiq.js.map +1 -1
- package/dist/PilotiqRegistry.d.ts +13 -0
- package/dist/PilotiqRegistry.d.ts.map +1 -1
- package/dist/PilotiqRegistry.js +15 -0
- package/dist/PilotiqRegistry.js.map +1 -1
- package/dist/pageData/misc.d.ts.map +1 -1
- package/dist/pageData/misc.js +6 -0
- package/dist/pageData/misc.js.map +1 -1
- package/dist/pageData/navigation.d.ts +1 -0
- package/dist/pageData/navigation.d.ts.map +1 -1
- package/dist/pageData/navigation.js +3 -0
- package/dist/pageData/navigation.js.map +1 -1
- package/dist/pageData/relationPages.d.ts.map +1 -1
- package/dist/pageData/relationPages.js +3 -0
- package/dist/pageData/relationPages.js.map +1 -1
- package/dist/pageData/resourcePages.d.ts.map +1 -1
- package/dist/pageData/resourcePages.js +8 -0
- package/dist/pageData/resourcePages.js.map +1 -1
- package/dist/react/AppShell.d.ts +8 -0
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/react/layouts/SidebarLayout.js +10 -2
- package/dist/react/layouts/SidebarLayout.js.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
- package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +25 -18
- package/dist/routes/relations.js.map +1 -1
- package/dist/routes/resources.js.map +1 -1
- package/package.json +10 -5
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- 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 -225
- 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 -194
- 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/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 -704
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1243
- 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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @pilotiq/pilotiq
|
|
2
2
|
|
|
3
|
+
## 0.24.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 02d5793: fix(pilotiq): panel routes resolve the live panel at request time — dev edits reflect without a server restart
|
|
8
|
+
|
|
9
|
+
Panel route handlers closed over the `Pilotiq` instance captured when their routes were registered. In dev, editing the panel module (or a resource/page schema it imports) re-registers a fresh panel in `PilotiqRegistry`, but those already-registered handler closures kept pointing at the stale instance — so SSR-rendered chrome and schema lagged a reload behind (the panel only updated after editing some _other_ watched file, or a restart).
|
|
10
|
+
|
|
11
|
+
The render-data layer now re-resolves the panel from `PilotiqRegistry` by name at request time, via a new `livePanel()` helper, applied at the top of `panelInfo` (chrome) and every render-data builder (`resourcePages`, `misc`, `relationPages`). This mirrors what `dispatchPageData()` already did for the client-nav path; the SSR route path was the only outlier. `livePanel()` falls back to the passed instance when the registry has no entry (tests, teardown), so non-dev behavior is unchanged.
|
|
12
|
+
|
|
13
|
+
- 539c87a: feat(pilotiq): ship `pilotiq-actions` boost skill — Phase B residual #1
|
|
14
|
+
|
|
15
|
+
First of the four still-open Phase B skill candidates. `SKILL.md` declares `appliesTo: ['@pilotiq/pilotiq']` so `@rudderjs/boost`'s `boost:install` writes it under `.ai/skills/` only when the consumer has `@pilotiq/pilotiq` installed. Trigger scopes to specific action-authoring contexts — adding header/row/bulk buttons, wiring a modal-form action, customizing per-row visibility, reaching for a built-in factory.
|
|
16
|
+
|
|
17
|
+
Three rule files under `boost/skills/pilotiq-actions/rules/`:
|
|
18
|
+
|
|
19
|
+
- **`dispatch-modes.md`** — 4 mutually-exclusive modes (`href` / `method` / `handler` / `submit`), modal-form as a flavor of handler, return shape, `ctx` shape, 12 modal chrome setters, `.confirm()` + `.formField()` interactions.
|
|
20
|
+
- **`visibility-and-authorization.md`** — 4 conditional setters (`visible / hidden / disabled / authorize`), `ActionVisibilityContext`, fail-closed semantics (opposite of layout-visible), per-row gating cost model, composing with Resource policies.
|
|
21
|
+
- **`factories.md`** — 25 pre-built factories (`create / edit / view / delete / replicate / restore / forceDelete / markAsRead`, bulk variants, `import / export`, relation\* variants including M2M `attach / detach`), `ReplicateOptions` with `getCreatedNotificationTitle / getRedirectUrl` callbacks, when to skip factories for compound flows.
|
|
22
|
+
|
|
23
|
+
Mirrors the shape established by `pilotiq-resource` / `pilotiq-fields` / `pilotiq-relations` / `pilotiq-tiptap-blocks`.
|
|
24
|
+
|
|
25
|
+
Remaining Phase B residuals (lower priority — `guidelines.md` already covers most of what they'd add): `pilotiq-widgets`, `pilotiq-theme`, `pilotiq-vite-plugin`.
|
|
26
|
+
|
|
27
|
+
## 0.24.2
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- 28fbd5f: feat(pilotiq): ship `boost/guidelines.md` for `@rudderjs/boost` discovery
|
|
32
|
+
|
|
33
|
+
Consumer Rudder apps with `@rudderjs/boost` installed now pick up `@pilotiq/pilotiq` AI coding guidelines automatically. Running `rudder boost:install` in the consumer writes the contents to `.ai/guidelines/pilotiq.md`, and the per-agent config files (`CLAUDE.md` / `.cursorrules` / `AGENTS.md` / etc.) include them in the concatenated guideline body.
|
|
34
|
+
|
|
35
|
+
The guidelines cover Resource definition (with `static model` auto-fill), folder-per-resource layout, the form-field catalog + common setters, layout primitives (Section / Tabs / Group / Wizard / Split / Fieldset), tables (columns + filters + groups + actions + reorder), Action with the four dispatch modes and modal-form variant, Page base classes (`ListPage` / `CreatePage` / `EditPage` / `ViewPage`) with override hooks, authorization via `Pilotiq.user()` + `can*` statics, Globals, Relations (`RelationManager` + `Repeater.relationship()`), reactive fields, theming, common pitfalls, and the key import surface.
|
|
36
|
+
|
|
37
|
+
Phase A of the boost-producer rollout — skills (`boost/skills/<name>/SKILL.md`) follow in subsequent releases. Adapter packages (`@pilotiq/tiptap` / `@pilotiq/codemirror` / `@pilotiq/recharts`) ship their own guidelines + `appliesTo`-gated skills separately.
|
|
38
|
+
|
|
39
|
+
- e36d65c: feat(pilotiq): ship boost skills — pilotiq-resource, pilotiq-fields, pilotiq-relations
|
|
40
|
+
|
|
41
|
+
Phase B of the boost-producer rollout. Adds three task-specific skill modules under `packages/pilotiq/boost/skills/`:
|
|
42
|
+
|
|
43
|
+
- **pilotiq-resource** — `SKILL.md` + 3 rules: defining-resources, page-overrides, authorization
|
|
44
|
+
- **pilotiq-fields** — `SKILL.md` + 3 rules: field-catalog (24 field types), validation (built-ins + `unique` + `distinct`), reactive-fields (`live` + `afterStateUpdated` + `$get`/`$set`)
|
|
45
|
+
- **pilotiq-relations** — `SKILL.md` + 2 rules: relation-managers (hasMany / morph / M2M), repeater-relationship (`Repeater.relationship` + `Builder.relationship`)
|
|
46
|
+
|
|
47
|
+
Each SKILL.md declares `appliesTo: ['@pilotiq/pilotiq']` so `@rudderjs/boost`'s `boost:install` only writes them to `.ai/skills/` when the consumer has `@pilotiq/pilotiq` installed. Triggers are scoped to specific work contexts — defining a Resource, adding a form field, wiring a relation — so AI agents load the deeper rule files on-demand rather than always-on.
|
|
48
|
+
|
|
49
|
+
Phase C (adapter packages — `@pilotiq/tiptap` / `@pilotiq/codemirror` / `@pilotiq/recharts`) and the remaining four skill candidates (pilotiq-actions, pilotiq-widgets, pilotiq-theme, pilotiq-vite-plugin) follow in subsequent releases.
|
|
50
|
+
|
|
51
|
+
- 6d2ac13: chore: slim published tarballs to `dist` + `boost` + `CHANGELOG.md`
|
|
52
|
+
|
|
53
|
+
All four packages now declare `"files": ["dist", "boost", "CHANGELOG.md"]` so npm pack only ships the compiled output, the `@rudderjs/boost` guidelines + skills tree, and the changelog. Previously `@pilotiq/pilotiq` shipped its full `src/`, `CLAUDE.md`, `.turbo/`, and test files; the three adapters shipped `src/` deliberately but no longer need to.
|
|
54
|
+
|
|
55
|
+
- **`@pilotiq/pilotiq`** — 2.1 MB → 1.3 MB (~38% smaller). Drops `src/**`, `CLAUDE.md`, `.turbo/` from the tarball.
|
|
56
|
+
- **`@pilotiq/tiptap` / `@pilotiq/codemirror` / `@pilotiq/recharts`** — drop `src/**` from the tarball.
|
|
57
|
+
|
|
58
|
+
No API impact. Consumer Tailwind `@source` rules that previously scanned `node_modules/@pilotiq/*/src` should re-point at `node_modules/@pilotiq/*/dist` (Tailwind scans `.js` just fine). Source maps in `dist/` still reference `../src/*.ts` paths that are no longer in the tarball — sourcemap navigation inside `node_modules` won't resolve to TS, but stack traces still line up.
|
|
59
|
+
|
|
3
60
|
## 0.24.1
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
# @pilotiq/pilotiq
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
View-based admin panel for RudderJS. A declarative class hierarchy (`Resource`, `Page`, `Action`, schema `Element`s) describes CRUD-managed entities; the framework auto-generates list / create / edit / view pages, table chrome, form validation + submit, and dropdown actions. The Vite plugin generates Vike page stubs at the panel's mount path; the runtime provider registers the routes. No code generation, no codegen step — declare the resource, register it on a panel, the pages exist.
|
|
6
|
+
|
|
7
|
+
Pilotiq sits on top of `@rudderjs/orm` (ergonomically — `Resource.model = ModelLike` auto-fills `Form.save` / `Form.loadRecord` / `Resource.deleteRecord` / `Table.records`) but doesn't hard-depend on it. Any `ModelLike` shape works (custom ORMs, testing doubles).
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
Two files, two lines each.
|
|
12
|
+
|
|
13
|
+
**`vite.config.ts`**
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { pilotiq } from '@pilotiq/pilotiq/vite'
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [
|
|
20
|
+
pilotiq(), // before @rudderjs/vite + vike
|
|
21
|
+
rudderjs(),
|
|
22
|
+
vike(),
|
|
23
|
+
tailwindcss(),
|
|
24
|
+
react(),
|
|
25
|
+
],
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**`bootstrap/providers.ts`**
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { pilotiq } from '@pilotiq/pilotiq'
|
|
33
|
+
import { adminPanel } from '../app/Pilotiq/AdminPanel.js'
|
|
34
|
+
|
|
35
|
+
export default [
|
|
36
|
+
...(await defaultProviders()),
|
|
37
|
+
pilotiq([adminPanel]),
|
|
38
|
+
] satisfies (new (app: Application) => ServiceProvider)[]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`@pilotiq/pilotiq` MUST be in `optimizeDeps.exclude` (it imports `node:fs` server-side and Vite must not pre-bundle for the browser).
|
|
42
|
+
|
|
43
|
+
## Key Patterns
|
|
44
|
+
|
|
45
|
+
### Defining a Resource
|
|
46
|
+
|
|
47
|
+
The shortest path: point `static model` at a `@rudderjs/orm` `Model` and the framework infers CRUD from the column metadata.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Resource, Form, Table, Column, TextField } from '@pilotiq/pilotiq'
|
|
51
|
+
import { Article } from '../Models/Article.js'
|
|
52
|
+
|
|
53
|
+
export class ArticleResource extends Resource {
|
|
54
|
+
static override label = 'Articles'
|
|
55
|
+
static override labelSingular = 'Article'
|
|
56
|
+
static override icon = 'file-text'
|
|
57
|
+
static override model = Article
|
|
58
|
+
|
|
59
|
+
static override form(form: Form): Form {
|
|
60
|
+
return form.schema([
|
|
61
|
+
TextField.make('title').required().placeholder('Article title…'),
|
|
62
|
+
TextField.make('slug').required(),
|
|
63
|
+
])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static override table(table: Table): Table {
|
|
67
|
+
return table
|
|
68
|
+
.columns([
|
|
69
|
+
Column.make('title').sortable().searchable(),
|
|
70
|
+
Column.make('slug').searchable(),
|
|
71
|
+
Column.make('createdAt').sortable().label('Created'),
|
|
72
|
+
])
|
|
73
|
+
.defaultSort('createdAt', 'desc')
|
|
74
|
+
.paginate(10)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
What you get for free with `static model`:
|
|
80
|
+
|
|
81
|
+
- **List**: `Table.records()` paginates `Article.query()`. `Column.searchable()` joins via `LIKE`/`orWhere`; `Column.sortable()` maps to `orderBy`.
|
|
82
|
+
- **Create**: `Form.save()` calls `Article.create(data)`.
|
|
83
|
+
- **Edit**: `Form.loadRecord(id)` calls `Article.find(id)`; `Form.save()` discriminates create vs update by the record's PK.
|
|
84
|
+
- **Delete**: `Resource.deleteRecord(id)` calls `Article.delete(id)`. Soft-deletes work out of the box if the Model declares `static softDeletes = true` AND the Resource declares `static softDeletes = true`.
|
|
85
|
+
- **Observers / mass-assignment / casts**: anything you set on the Model carries through.
|
|
86
|
+
|
|
87
|
+
Register on the panel:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
Pilotiq.make('Admin').path('/admin').resources([ArticleResource])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This gives working list / create / edit / view pages at `/admin/articles*`. Every method on `Resource` is **static** — Resources register as classes, not instances.
|
|
94
|
+
|
|
95
|
+
### Folder-per-resource layout (for non-trivial resources)
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
app/Pilotiq/Articles/
|
|
99
|
+
├── ArticleResource.ts # binds form / table / detail / pages
|
|
100
|
+
├── Pages/
|
|
101
|
+
│ ├── ListArticles.ts # extends ListPage
|
|
102
|
+
│ ├── CreateArticle.ts # extends CreatePage
|
|
103
|
+
│ ├── EditArticle.ts # extends EditPage
|
|
104
|
+
│ └── ViewArticle.ts # extends ViewPage
|
|
105
|
+
├── Schemas/
|
|
106
|
+
│ ├── form.ts # exported form() function
|
|
107
|
+
│ └── table.ts # exported table() function
|
|
108
|
+
└── RelationManagers/
|
|
109
|
+
└── CommentsRelationManager.ts
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Form Fields
|
|
113
|
+
|
|
114
|
+
| Field | Renders |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `TextField` | `<input type="text">` — adds `password()`, `revealable()`, `copyable()`, `mask()`, `prefixAction()`, `suffixAction()` |
|
|
117
|
+
| `EmailField` | `<input type="email">` (auto `email()` validator) |
|
|
118
|
+
| `NumberField` | `<input type="number">` + `min() / max() / step()` |
|
|
119
|
+
| `Slider` | range track |
|
|
120
|
+
| `Textarea` | `<textarea>` + `autosize()` / `rows()` |
|
|
121
|
+
| `MarkdownField` | textarea + preview tabs |
|
|
122
|
+
| `RichTextField` | Tiptap editor (`@pilotiq/tiptap`) |
|
|
123
|
+
| `CodeEditorField` | CodeMirror 6 (`@pilotiq/codemirror`) |
|
|
124
|
+
| `SelectField` | searchable Select; `options(arr \| fn)`, `createOptionForm()` for inline-create |
|
|
125
|
+
| `RadioField` | radio stack |
|
|
126
|
+
| `ToggleButtons` | chip-style segmented (sugar over Radio) |
|
|
127
|
+
| `CheckboxField` | single checkbox (bool) |
|
|
128
|
+
| `CheckboxList` | checkbox stack (`string[]`) |
|
|
129
|
+
| `ToggleField` | switch (bool) |
|
|
130
|
+
| `TagsInput` | chip multi-tag (`string[]`) |
|
|
131
|
+
| `KeyValueField` | key/value rows (`Record<string, string>`) |
|
|
132
|
+
| `DateField` / `DateTimePicker` | calendar popovers |
|
|
133
|
+
| `ColorPicker` | hex input + swatch |
|
|
134
|
+
| `FileUpload` | drop zone (needs `Pilotiq.uploads({ adapter })`) |
|
|
135
|
+
| `Repeater` | array-of-subschema with reorderable rows |
|
|
136
|
+
| `Builder` | heterogeneous Repeater — each row is one of N block types |
|
|
137
|
+
| `HiddenField` | submitted but not rendered |
|
|
138
|
+
|
|
139
|
+
Every field inherits these setters from `Field`:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
Field.make('name')
|
|
143
|
+
.label('Display label')
|
|
144
|
+
.helperText('Shown below the input')
|
|
145
|
+
.placeholder('e.g. Hello world')
|
|
146
|
+
.default('initial value')
|
|
147
|
+
.prefix('$') // or .prefix({ icon: 'dollar' })
|
|
148
|
+
.suffix('USD')
|
|
149
|
+
.required()
|
|
150
|
+
.validate([Field.email(), Field.unique({ model: User })])
|
|
151
|
+
.visible(({ user }) => user.role === 'admin')
|
|
152
|
+
.hidden(rule)
|
|
153
|
+
.disabled(rule)
|
|
154
|
+
.columnSpan(2) // inside a Grid / Section.columns(n)
|
|
155
|
+
.live() // re-resolve schema on change
|
|
156
|
+
.afterStateUpdated((value, ctx) => ctx.$set('slug', slugify(value)))
|
|
157
|
+
.dehydrated(false) // don't submit
|
|
158
|
+
.autofocus()
|
|
159
|
+
.hiddenLabel()
|
|
160
|
+
.disabledOn(['edit']) // page-mode sugar
|
|
161
|
+
.hiddenOn(['view'])
|
|
162
|
+
.visibleOn(['create', 'edit'])
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Form Layouts
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
Section.make('Customer details')
|
|
169
|
+
.description('Name and contact info')
|
|
170
|
+
.icon('user')
|
|
171
|
+
.columns(2)
|
|
172
|
+
.schema([
|
|
173
|
+
TextField.make('first'),
|
|
174
|
+
TextField.make('last'),
|
|
175
|
+
TextField.make('email').columnSpan(2),
|
|
176
|
+
])
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Section setters: `.description(text)`, `.icon(name)`, `.badge(text)`, `.aside()` (right rail under `Split`), `.compact()`, `.dense()`, `.secondary()` (muted), `.collapsible()`, `.collapsed()`, `.persistCollapsed('key')`, `.afterHeader([Action…])`.
|
|
180
|
+
|
|
181
|
+
Other layout primitives:
|
|
182
|
+
|
|
183
|
+
| Element | Use case |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `Grid` | Named CSS grid; children declare `columnSpan(n)` / `columnStart(n)` |
|
|
186
|
+
| `Group` | Chrome-less wrapper — useful for visibility gating without a border |
|
|
187
|
+
| `Fieldset` | `<fieldset><legend>` semantics, lighter than Section |
|
|
188
|
+
| `Split` | Two-column layout; second child auto-routes to right-rail (or use `Section.aside()`) |
|
|
189
|
+
| `Tabs` | Tab strip; each `Tab.make(name).schema([…])` is one panel |
|
|
190
|
+
| `Wizard` | Multi-step form. Each `Step.make().schema(...)` validates before advancing |
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
Wizard.make()
|
|
194
|
+
.steps([
|
|
195
|
+
Step.make('Account') .icon('user') .schema([...]),
|
|
196
|
+
Step.make('Billing') .icon('credit-card').schema([...]),
|
|
197
|
+
Step.make('Confirm') .icon('check') .schema([...]),
|
|
198
|
+
])
|
|
199
|
+
.skippable() // allow forward without saving
|
|
200
|
+
.startOnStep(0)
|
|
201
|
+
.persist(false) // disable URL ?step= persistence
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Tables
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
table
|
|
208
|
+
.columns([
|
|
209
|
+
Column.make('title').sortable().searchable(),
|
|
210
|
+
Column.make('status').badge().colors({ draft: 'gray', live: 'success' }),
|
|
211
|
+
Column.make('createdAt').sortable().since().label('Created'),
|
|
212
|
+
])
|
|
213
|
+
.defaultSort('createdAt', 'desc')
|
|
214
|
+
.paginate(10)
|
|
215
|
+
.filters([
|
|
216
|
+
SelectFilter.make('status').options({ draft: 'Draft', live: 'Live' }),
|
|
217
|
+
TernaryFilter.make('isPublished').label('Published?'),
|
|
218
|
+
TrashedFilter.make(), // when softDeletes = true
|
|
219
|
+
])
|
|
220
|
+
.actions([Action.edit, Action.delete])
|
|
221
|
+
.bulkActions([Action.bulkDelete])
|
|
222
|
+
.reorderable('position') // drag-handle reorder + persistence
|
|
223
|
+
.persistFiltersInSession() // 302-restore stored URL slice
|
|
224
|
+
.groups([
|
|
225
|
+
TableGroup.make('status').collapsible(),
|
|
226
|
+
])
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Column types: `Column` (text, default), `ImageColumn`, `IconColumn`, `BadgeColumn` / `Column.badge()`, `ColorColumn`, `TextColumn` (rich-display: words/chars/bulleted/markdown/html), `IconColumn`. Common setters: `.sortable()`, `.searchable()`, `.label()`, `.copyable()`, `.tooltip()`, `.formatStateUsing(v => …)`, `.url(rec => '/path')`.
|
|
230
|
+
|
|
231
|
+
Filters: `Filter` (generic), `SelectFilter`, `TernaryFilter` (yes/no/all), `TrashedFilter`, `QueryBuilderFilter` (runtime constraint builder).
|
|
232
|
+
|
|
233
|
+
### Actions
|
|
234
|
+
|
|
235
|
+
`Action` is the unified primitive for buttons — header / row / bulk / form-submit / dropdown items. Same builder, four placement modes (`inline | row | header | bulk`), four mutually-exclusive dispatch modes (`href / method / handler / submit`).
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
Action.make('publish')
|
|
239
|
+
.label('Publish')
|
|
240
|
+
.color('success')
|
|
241
|
+
.icon('send')
|
|
242
|
+
.visible(({ record }) => record.status === 'draft')
|
|
243
|
+
.handler(async (ctx) => {
|
|
244
|
+
await ctx.record.publish()
|
|
245
|
+
return { notify: Notification.success('Published') }
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Built-in factories: `Action.create(...)`, `Action.edit(R, base, id)`, `Action.delete()`, `Action.replicate(R, base, id)`, `Action.restore(...)`, `Action.forceDelete(...)`, `Action.export(...)`, `Action.import(...)`, `Action.bulkDelete()`, `Action.relation*(...)` (attach / detach / bulkDetach).
|
|
250
|
+
|
|
251
|
+
Add `.schema([…])` to make the action open a form-modal:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
Action.make('reschedule')
|
|
255
|
+
.icon('calendar')
|
|
256
|
+
.schema([DateTimePicker.make('at').required()])
|
|
257
|
+
.handler(async (ctx) => {
|
|
258
|
+
await ctx.record.reschedule(ctx.values.at)
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Modal chrome setters: `.modalHeading(...)`, `.modalDescription(...)`, `.modalIcon(...)`, `.modalWidth('sm'|'md'|'lg'|'xl')`, `.slideOver()`, `.stickyModalHeader()`, `.closeModalByEscaping(false)`, `.modalContentFooter([Alert, …])`.
|
|
263
|
+
|
|
264
|
+
Group actions in a dropdown:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
ActionGroup.make('more').label('More').icon('more-horizontal').actions([
|
|
268
|
+
Action.make('export').handler(…),
|
|
269
|
+
Action.make('clone').handler(…),
|
|
270
|
+
])
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Pages
|
|
274
|
+
|
|
275
|
+
A Page is "anything with a `schema()`" — the unit of routing, content, and lifecycle. The base `Page` class covers custom pages registered via `panel.pages([AnalyticsPage])`; resource-bound pages can additionally extend `ListPage`, `CreatePage`, `EditPage`, or `ViewPage` to expose override hooks (`getHeader`, `getFormActions`, `beforeCreate`, `afterUpdate`, etc.).
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import { Page, Heading, Card, Text } from '@pilotiq/pilotiq'
|
|
279
|
+
|
|
280
|
+
class AnalyticsPage extends Page {
|
|
281
|
+
static override slug = 'analytics' // optional — derived from class name
|
|
282
|
+
static override label = 'Analytics'
|
|
283
|
+
static override icon = 'bar-chart-3'
|
|
284
|
+
|
|
285
|
+
static override schema(ctx) {
|
|
286
|
+
return [
|
|
287
|
+
Heading.make('Site analytics').level(1),
|
|
288
|
+
Card.make().schema([
|
|
289
|
+
Text.make('Stats coming soon.'),
|
|
290
|
+
]),
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Resource page overrides — `static pages()` returns the four (or fewer) bases:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
class ArticleResource extends Resource {
|
|
300
|
+
static override pages() {
|
|
301
|
+
return {
|
|
302
|
+
list: ListArticles,
|
|
303
|
+
create: CreateArticle,
|
|
304
|
+
edit: EditArticle,
|
|
305
|
+
view: ViewArticle,
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
class CreateArticle extends CreatePage {
|
|
311
|
+
static override beforeCreate(data, ctx) {
|
|
312
|
+
data.authorId = ctx.user.id
|
|
313
|
+
return data
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
class EditArticle extends EditPage {
|
|
318
|
+
static override getFormActions(actions: Action[]): Action[] {
|
|
319
|
+
return [
|
|
320
|
+
...actions,
|
|
321
|
+
Action.make('preview').href(`/preview/${'${ctx.record.id}'}`).color('ghost'),
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Wizard create-page:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
class CreateArticle extends CreatePage {
|
|
331
|
+
static override getSteps() {
|
|
332
|
+
return [
|
|
333
|
+
Step.make('Content') .schema([TextField.make('title'), MarkdownField.make('body')]),
|
|
334
|
+
Step.make('Publish') .schema([DateField.make('publishAt'), ToggleField.make('isPublic')]),
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Authorization
|
|
341
|
+
|
|
342
|
+
Resources declare `can*` statics; the framework calls them with the resolved user from `Pilotiq.user(req => …)`. Routes fail closed (403 if any predicate returns false).
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
Pilotiq.make('Admin')
|
|
346
|
+
.path('/admin')
|
|
347
|
+
.user(async (req) => req.session?.user ?? null)
|
|
348
|
+
.resources([ArticleResource])
|
|
349
|
+
|
|
350
|
+
class ArticleResource extends Resource {
|
|
351
|
+
static override canAccess(user) { return Boolean(user) }
|
|
352
|
+
static override canView(user, record) { return user.id === record.authorId || user.role === 'admin' }
|
|
353
|
+
static override canCreate(user) { return user.role !== 'reader' }
|
|
354
|
+
static override canEdit(user, record) { return user.id === record.authorId }
|
|
355
|
+
static override canDelete(user, record) { return user.role === 'admin' }
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Per-record gates on collection pages stamp `_visibleActions` / `_disabledActions` on each row before rendering, so server-side eval results are consistent with what the client renders.
|
|
360
|
+
|
|
361
|
+
### Globals (singleton resources)
|
|
362
|
+
|
|
363
|
+
`Global` is a singleton-shaped Resource. Same builder, minus list/create/delete; routes are `GET/POST {base}/{slug}` for edit, `GET {base}/{slug}/view` for view.
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
class SiteSettings extends Global {
|
|
367
|
+
static override label = 'Site Settings'
|
|
368
|
+
static override icon = 'settings'
|
|
369
|
+
static override model = Settings
|
|
370
|
+
static override navigationGroup = 'Settings' // default
|
|
371
|
+
|
|
372
|
+
static override form(form) {
|
|
373
|
+
return form.schema([
|
|
374
|
+
TextField.make('siteName').required(),
|
|
375
|
+
Textarea.make('description'),
|
|
376
|
+
ColorPicker.make('brandColor'),
|
|
377
|
+
])
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
static override canAccess(user) { return user.role === 'admin' }
|
|
381
|
+
static override canEdit(user) { return user.role === 'admin' }
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Relations
|
|
386
|
+
|
|
387
|
+
`Resource.relations()` returns `RelationManager[]`. Each manager renders as a tab on the parent resource's view/edit page.
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
class ArticleResource extends Resource {
|
|
391
|
+
static override relations(): RelationManagerClass[] {
|
|
392
|
+
return [CommentsRelationManager, TagsRelationManager]
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
class CommentsRelationManager extends RelationManager {
|
|
397
|
+
static override relationName = 'comments' // Article.comments() relation
|
|
398
|
+
static override label = 'Comments'
|
|
399
|
+
static override icon = 'message-square'
|
|
400
|
+
|
|
401
|
+
static override table(table) {
|
|
402
|
+
return table.columns([
|
|
403
|
+
Column.make('body').searchable(),
|
|
404
|
+
Column.make('author.name').label('Author'),
|
|
405
|
+
Column.make('createdAt').since(),
|
|
406
|
+
])
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
static override form(form) {
|
|
410
|
+
return form.schema([Textarea.make('body').required()])
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Supports `hasMany`, `belongsToMany` (with pivot extras via `.pivotColumns([…])`), `morphMany`, `morphTo`, `morphToMany`, `morphedByMany`. Use `Action.relationAttach()`, `Action.relationDetach()`, `Action.relationBulkDetach()` for M2M attach/detach UI.
|
|
416
|
+
|
|
417
|
+
Repeater fields can also be relation-backed via `.relationship('comments')` — same `hasMany`/`morph*`/`M2M` set.
|
|
418
|
+
|
|
419
|
+
### Reactive fields
|
|
420
|
+
|
|
421
|
+
`.live()` re-resolves the schema on change; `afterStateUpdated()` fires server-side imperative updates against `$state`:
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
TextField.make('title')
|
|
425
|
+
.live()
|
|
426
|
+
.afterStateUpdated((title, ctx) => ctx.$set('slug', slugify(title)))
|
|
427
|
+
|
|
428
|
+
SelectField.make('country').options(countries).live()
|
|
429
|
+
SelectField.make('region').options(({ $get }) => {
|
|
430
|
+
const country = $get('country')
|
|
431
|
+
return country ? regionsFor(country) : {}
|
|
432
|
+
})
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Multi-form pages (record-page with multiple panels) must pin `formId` explicitly: `Form.make().formId('details')`. The auto-fallback covers single-form pages.
|
|
436
|
+
|
|
437
|
+
Client-only reactivity (no server round-trip): `afterStateUpdatedJs(`$set('slug', $state.title.toLowerCase().replace(/\s+/g, '-'))`)`.
|
|
438
|
+
|
|
439
|
+
### Theming / Branding
|
|
440
|
+
|
|
441
|
+
```ts
|
|
442
|
+
Pilotiq.make('Admin')
|
|
443
|
+
.branding({ name: 'Acme', logo: '/logo.svg', primaryColor: '#3b82f6' })
|
|
444
|
+
.theme('nova') // built-in preset
|
|
445
|
+
.layout('sidebar', { variant: 'inset', collapsible: 'icon', side: 'left' }) // see below
|
|
446
|
+
.use(themeEditor()) // exposes /admin/theme runtime editor
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Presets: `default`, `nova`, `maia`, `lyra`. Custom themes pass a `Theme` object to `.theme()`.
|
|
450
|
+
|
|
451
|
+
**`.layout(mode, opts?)`** — `mode` is `'sidebar' | 'topbar'`. The optional second arg configures the sidebar's chrome and is **bound to the `'sidebar'` mode**: `.layout('topbar', {...})` is a compile error (so sidebar-only config can't silently no-op under topbar). Sidebar keys (all optional):
|
|
452
|
+
- `variant`: `'inset'` (default — content floats in a rounded card) · `'floating'` (sidebar itself floats) · `'sidebar'` (classic full-height flush rail)
|
|
453
|
+
- `collapsible`: `'icon'` (default — collapses to an icon rail) · `'offcanvas'` (slides fully off-screen) · `'none'` (always expanded)
|
|
454
|
+
- `side`: `'left'` (default, RTL-aware) · `'right'`
|
|
455
|
+
|
|
456
|
+
Plugin-shaped extensions: `.plugins([tiptap(), codeEditor(), recharts()])` — register adapter packages in one call.
|
|
457
|
+
|
|
458
|
+
## Common Pitfalls
|
|
459
|
+
|
|
460
|
+
- **`@pilotiq/pilotiq` must be in `optimizeDeps.exclude`.** Pre-bundling pulls `node:fs` into the browser bundle and crashes the dev server with a `node:fs` client error.
|
|
461
|
+
- **`static`, not instance.** Every Resource / Page / RelationManager method (`form`, `table`, `relations`, `canAccess`, …) is static. The framework invokes `R.form(…)` directly — instance methods don't run.
|
|
462
|
+
- **Folder-per-resource for non-trivial resources.** Inlining everything in `index.ts` becomes unwieldy past two columns of code. Split `Pages/`, `Schemas/`, `RelationManagers/` early.
|
|
463
|
+
- **Soft-deletes are two-sided.** `Model.softDeletes = true` (in `@rudderjs/orm`) AND `Resource.softDeletes = true` are both required. Setting only one silently drops the restore/force-delete UI without an error.
|
|
464
|
+
- **Multi-form record pages must pin `formId`.** `Form.make().formId('details')` is required when more than one form lives on a single page; otherwise live-resolves cross-talk between forms.
|
|
465
|
+
- **Use real per-cell `<a href>` over row `onClick`.** `Column.url(rec => …)` preserves cmd-click, new-tab, middle-click, and screen-reader semantics. Row-level click handlers break all of those.
|
|
466
|
+
- **Manual provider composition is fine — auto-discovery is optional.** `bootstrap/providers.ts` can return an explicit array `[pilotiq([panel]), ...]` without invoking `rudder providers:discover`. The doctor warn for "providers manifest missing" doesn't apply.
|
|
467
|
+
- **Auto-generated `pages/(pilotiq)/` stubs are gitignored.** Never commit them — the Vite plugin regenerates them on every dev start.
|
|
468
|
+
|
|
469
|
+
## Key Imports
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
import {
|
|
473
|
+
// Builder + provider
|
|
474
|
+
Pilotiq,
|
|
475
|
+
pilotiq, // service-provider factory
|
|
476
|
+
|
|
477
|
+
// Core classes
|
|
478
|
+
Resource,
|
|
479
|
+
Global,
|
|
480
|
+
Page,
|
|
481
|
+
ListPage,
|
|
482
|
+
CreatePage,
|
|
483
|
+
EditPage,
|
|
484
|
+
ViewPage,
|
|
485
|
+
RelationManager,
|
|
486
|
+
Cluster, // group resources under a URL prefix
|
|
487
|
+
Notification,
|
|
488
|
+
AdminPanel,
|
|
489
|
+
|
|
490
|
+
// Schema base + layouts
|
|
491
|
+
Form,
|
|
492
|
+
Table,
|
|
493
|
+
Section,
|
|
494
|
+
Tabs,
|
|
495
|
+
Tab,
|
|
496
|
+
Group,
|
|
497
|
+
Fieldset,
|
|
498
|
+
Split,
|
|
499
|
+
Grid,
|
|
500
|
+
Wizard,
|
|
501
|
+
Step,
|
|
502
|
+
Repeater,
|
|
503
|
+
Builder,
|
|
504
|
+
Heading,
|
|
505
|
+
Text,
|
|
506
|
+
Alert,
|
|
507
|
+
Card,
|
|
508
|
+
Divider,
|
|
509
|
+
UnorderedList,
|
|
510
|
+
|
|
511
|
+
// Fields
|
|
512
|
+
Field,
|
|
513
|
+
TextField,
|
|
514
|
+
EmailField,
|
|
515
|
+
NumberField,
|
|
516
|
+
Slider,
|
|
517
|
+
Textarea,
|
|
518
|
+
MarkdownField,
|
|
519
|
+
SelectField,
|
|
520
|
+
RadioField,
|
|
521
|
+
ToggleButtons,
|
|
522
|
+
CheckboxField,
|
|
523
|
+
CheckboxList,
|
|
524
|
+
ToggleField,
|
|
525
|
+
TagsInput,
|
|
526
|
+
KeyValueField,
|
|
527
|
+
DateField,
|
|
528
|
+
DateTimePicker,
|
|
529
|
+
ColorPicker,
|
|
530
|
+
FileUpload,
|
|
531
|
+
HiddenField,
|
|
532
|
+
|
|
533
|
+
// Tables
|
|
534
|
+
Column,
|
|
535
|
+
ImageColumn,
|
|
536
|
+
IconColumn,
|
|
537
|
+
BadgeColumn,
|
|
538
|
+
ColorColumn,
|
|
539
|
+
TextColumn,
|
|
540
|
+
TableGroup,
|
|
541
|
+
|
|
542
|
+
// Filters
|
|
543
|
+
Filter,
|
|
544
|
+
SelectFilter,
|
|
545
|
+
TernaryFilter,
|
|
546
|
+
TrashedFilter,
|
|
547
|
+
QueryBuilderFilter,
|
|
548
|
+
|
|
549
|
+
// Actions
|
|
550
|
+
Action,
|
|
551
|
+
ActionGroup,
|
|
552
|
+
|
|
553
|
+
// Infolist entries (read-only display)
|
|
554
|
+
Entry,
|
|
555
|
+
TextEntry,
|
|
556
|
+
BadgeEntry,
|
|
557
|
+
IconEntry,
|
|
558
|
+
ImageEntry,
|
|
559
|
+
KeyValueEntry,
|
|
560
|
+
ColorEntry,
|
|
561
|
+
RepeatableEntry,
|
|
562
|
+
} from '@pilotiq/pilotiq'
|
|
563
|
+
|
|
564
|
+
// Subpaths
|
|
565
|
+
import { pilotiq } from '@pilotiq/pilotiq/vite' // vite plugin
|
|
566
|
+
import { useNavigate } from '@pilotiq/pilotiq/react' // SPA navigate
|
|
567
|
+
import { localUpload, s3Upload } from '@pilotiq/pilotiq/uploads' // upload adapters
|
|
568
|
+
import { themeEditor } from '@pilotiq/pilotiq/plugins' // built-in plugins
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Adapter packages (separate npm packages — install only what you use): `@pilotiq/tiptap` (RichTextField + custom blocks), `@pilotiq/codemirror` (CodeEditorField), `@pilotiq/recharts` (Chart widget).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pilotiq-actions
|
|
3
|
+
description: Buttons, modal-form actions, and built-in CRUD/import/export/relation factories in pilotiq — the 4 dispatch modes, visibility/authorize layers, and per-row gating
|
|
4
|
+
license: MIT
|
|
5
|
+
appliesTo:
|
|
6
|
+
- '@pilotiq/pilotiq'
|
|
7
|
+
trigger: adding header / row / bulk action buttons to a `Resource.table()` / page, wiring a modal-form action, customizing per-row visibility via `visible()` / `authorize()`, or using one of the `Action.*` factories (`create` / `edit` / `delete` / `replicate` / `import` / `export` / `relation*` / `bulk*`)
|
|
8
|
+
skip: customizing the form schema itself (use `pilotiq-fields`) or wiring a relation tab as a whole (use `pilotiq-relations`)
|
|
9
|
+
metadata:
|
|
10
|
+
author: pilotiq
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Pilotiq Actions
|
|
14
|
+
|
|
15
|
+
## When to use this skill
|
|
16
|
+
|
|
17
|
+
Load when you're:
|
|
18
|
+
|
|
19
|
+
- Adding action buttons to a page — header actions, row actions, bulk actions, inline actions
|
|
20
|
+
- Wiring a modal-form action (`Action.make('foo').schema([Field…]).handler(ctx => …)`)
|
|
21
|
+
- Customizing chrome (colors, sizes, icons, tooltips, outlined / icon-only / destructive)
|
|
22
|
+
- Setting visibility / disabled / authorize rules — including per-row gating that re-evaluates on every list row
|
|
23
|
+
- Reaching for a built-in factory: `Action.create / edit / view / delete / replicate / import / export / restore / forceDelete / markAsRead / bulkDelete / bulkRestore / bulkForceDelete / bulkReplicate / bulkExport / relation*`
|
|
24
|
+
|
|
25
|
+
For the form schema *inside* an action's modal, that's `pilotiq-fields`. For the resource-side `Resource.table().recordActions([…])` wiring, this skill covers the action surface itself; `pilotiq-resource` covers when to subclass `ListPage` to override `getHeaderActions()` etc.
|
|
26
|
+
|
|
27
|
+
## Quick Reference
|
|
28
|
+
|
|
29
|
+
| Task | Open |
|
|
30
|
+
|---|---|
|
|
31
|
+
| 4 dispatch modes — `href` / `method` / `handler` / `submit`; modal-form via `.schema([…]).handler()`; `formField` for submit-button pairs | `rules/dispatch-modes.md` |
|
|
32
|
+
| Visibility, disabled, authorize — `ActionVisibilityContext`, per-row gating, fail-closed semantics, async predicates | `rules/visibility-and-authorization.md` |
|
|
33
|
+
| Built-in factories — `Action.create / .edit / .delete / .replicate / .import / .export / .relation*` plus bulk variants; chrome + visibility defaults | `rules/factories.md` |
|
|
34
|
+
|
|
35
|
+
## Key concepts (load once)
|
|
36
|
+
|
|
37
|
+
- **Every action is `Action.make(name).<setters>`.** No subclassing. The `name` is the discriminator for visibility lookups + the dispatch URL slug.
|
|
38
|
+
- **Four dispatch modes are mutually exclusive.** `.href(url)` = link. `.method('POST').action(url)` = form-post. `.handler(ctx => …)` = JSON dispatch via the framework's `_action/:name` route. `.submit()` = trigger the enclosing form's submit.
|
|
39
|
+
- **Modal-form is a flavor of handler.** Calling `.schema([Field, …])` flips an action into modal-form mode: clicking the trigger opens a Dialog with the schema as a real pilotiq form; submit fetches the action's dispatch URL with the form body.
|
|
40
|
+
- **Placement determines where the button mounts.** `inline` (default, in-page) / `bulk` (toolbar when rows selected) / `row` (per-row) / `header` (page header). Placement is implicit from how you pass the action — `Resource.table().recordActions([…])` are row, `.bulkActions([…])` are bulk, etc.
|
|
41
|
+
- **Visibility is fail-closed.** `.visible(rule)` / `.hidden(rule)` / `.disabled(rule)` / `.authorize(rule)` accept `boolean | (ctx) => boolean | Promise<boolean>`. Throwing → visibility false. `ActionVisibilityContext` carries `{ record?, records?, user? }` depending on placement.
|
|
42
|
+
- **Row-placement actions evaluate per row.** The framework calls each row's predicates in `loadTableRecords` and stamps `row._visibleActions: name[]` / `row._disabledActions: name[]`. The renderer filters its action strip against that stamp.
|
|
43
|
+
- **All non-modal handler dispatches SPA-update.** Fetch with `Accept: application/json`, drain notifications via `useToast()`, then `useNavigate(redirect)`. No page reload. Only form-post `method` actions (e.g. `Action.delete`) still use the 303-redirect path for back-compat.
|
|
44
|
+
|
|
45
|
+
## Examples
|
|
46
|
+
|
|
47
|
+
- `playground/app/Pilotiq/Articles/ArticleResource.ts` — header/row actions with built-in factories.
|
|
48
|
+
- `playground/app/Pilotiq/Posts/PostResource.ts` — modal-form action (`Action.make('publish').schema([…]).handler(…)`).
|
|
49
|
+
- `playground/app/Pilotiq/Users/UserResource.ts` — `Action.replicate` with `beforeReplicaSaved` mutator.
|