@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
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../schema/Element.js'
|
|
3
|
-
import type { StatColor, StatMeta } from '../../schema/Stat.js'
|
|
4
|
-
import { useWidgetData } from '../WidgetDataContext.js'
|
|
5
|
-
import { useIconFor } from '../icon-context.js'
|
|
6
|
-
import type { SerializedIcon } from '../../icons/types.js'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Plan #15 Phase B — `StatsOverviewRenderer`.
|
|
10
|
-
*
|
|
11
|
-
* Reads the resolved `_widgetData[id]` payload through `useWidgetData`,
|
|
12
|
-
* lays out a card grid keyed off `meta.columns`, and falls back to a
|
|
13
|
-
* skeleton row while the lazy fetch is in flight.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const COLOR_CARD_CLASSES: Record<StatColor, string> = {
|
|
17
|
-
default: 'border-border',
|
|
18
|
-
primary: 'border-primary/30',
|
|
19
|
-
success: 'border-emerald-500/40',
|
|
20
|
-
warning: 'border-amber-500/40',
|
|
21
|
-
destructive: 'border-red-500/40',
|
|
22
|
-
info: 'border-blue-500/40',
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const COLOR_VALUE_CLASSES: Record<StatColor, string> = {
|
|
26
|
-
default: 'text-foreground',
|
|
27
|
-
primary: 'text-primary',
|
|
28
|
-
success: 'text-emerald-600 dark:text-emerald-400',
|
|
29
|
-
warning: 'text-amber-600 dark:text-amber-400',
|
|
30
|
-
destructive: 'text-red-600 dark:text-red-400',
|
|
31
|
-
info: 'text-blue-600 dark:text-blue-400',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const COLOR_SPARKLINE_CLASSES: Record<StatColor, string> = {
|
|
35
|
-
default: 'text-muted-foreground',
|
|
36
|
-
primary: 'text-primary',
|
|
37
|
-
success: 'text-emerald-500',
|
|
38
|
-
warning: 'text-amber-500',
|
|
39
|
-
destructive: 'text-red-500',
|
|
40
|
-
info: 'text-blue-500',
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface StatsOverviewRendererProps {
|
|
44
|
-
meta: ElementMeta
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function StatsOverviewRenderer({ meta }: StatsOverviewRendererProps) {
|
|
48
|
-
const columns = clampColumns(meta['columns'])
|
|
49
|
-
const { data, error, isLoading } = useWidgetData(meta as Parameters<typeof useWidgetData>[0])
|
|
50
|
-
|
|
51
|
-
if (isLoading) return <StatsSkeleton columns={columns} />
|
|
52
|
-
if (error) return <StatsError message={error} columns={columns} />
|
|
53
|
-
|
|
54
|
-
const stats = readStats(data)
|
|
55
|
-
if (stats.length === 0) return null
|
|
56
|
-
return (
|
|
57
|
-
<div
|
|
58
|
-
className="grid gap-4"
|
|
59
|
-
style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}
|
|
60
|
-
>
|
|
61
|
-
{stats.map((s, i) => <StatCard key={i} stat={s} />)}
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function clampColumns(raw: unknown): number {
|
|
67
|
-
const n = Number(raw)
|
|
68
|
-
if (!Number.isFinite(n) || n <= 0) return 3
|
|
69
|
-
return Math.min(6, Math.max(1, Math.floor(n)))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function readStats(data: unknown): StatMeta[] {
|
|
73
|
-
if (!data || typeof data !== 'object') return []
|
|
74
|
-
const stats = (data as { stats?: unknown }).stats
|
|
75
|
-
return Array.isArray(stats) ? (stats as StatMeta[]) : []
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ─── Card ─────────────────────────────────────────────────
|
|
79
|
-
|
|
80
|
-
interface StatCardProps {
|
|
81
|
-
stat: StatMeta
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function StatCard({ stat }: StatCardProps) {
|
|
85
|
-
const color = stat.color ?? 'default'
|
|
86
|
-
const cardClass = `rounded-xl border bg-card p-5 shadow-sm transition ${COLOR_CARD_CLASSES[color]}`
|
|
87
|
-
const valueClass = `text-3xl font-semibold tracking-tight tabular-nums ${COLOR_VALUE_CLASSES[color]}`
|
|
88
|
-
|
|
89
|
-
const Inner = (
|
|
90
|
-
<div className="flex flex-col gap-2">
|
|
91
|
-
<div className="flex items-start justify-between gap-3">
|
|
92
|
-
<p className="text-sm font-medium text-muted-foreground">{stat.label}</p>
|
|
93
|
-
{stat.icon && <StatIcon name={stat.icon} className={`size-5 ${COLOR_VALUE_CLASSES[color]}`} />}
|
|
94
|
-
</div>
|
|
95
|
-
<p className={valueClass}>{formatValue(stat.value)}</p>
|
|
96
|
-
{(stat.description || stat.descriptionIcon) && (
|
|
97
|
-
<StatDescription stat={stat} />
|
|
98
|
-
)}
|
|
99
|
-
{stat.chart && stat.chart.length > 0 && (
|
|
100
|
-
<Sparkline values={stat.chart} className={COLOR_SPARKLINE_CLASSES[color]} />
|
|
101
|
-
)}
|
|
102
|
-
</div>
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
const extraAttrs = (stat.extraAttributes ?? {}) as Record<string, unknown>
|
|
106
|
-
|
|
107
|
-
if (stat.url) {
|
|
108
|
-
return (
|
|
109
|
-
<a
|
|
110
|
-
href={stat.url}
|
|
111
|
-
target={stat.openInNewTab ? '_blank' : undefined}
|
|
112
|
-
rel={stat.openInNewTab ? 'noopener noreferrer' : undefined}
|
|
113
|
-
className={`${cardClass} block hover:border-foreground/30 hover:shadow-md`}
|
|
114
|
-
{...extraAttrs}
|
|
115
|
-
>
|
|
116
|
-
{Inner}
|
|
117
|
-
</a>
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return <div className={cardClass} {...extraAttrs}>{Inner}</div>
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function formatValue(v: StatMeta['value']): string {
|
|
125
|
-
if (v === null || v === undefined) return '—'
|
|
126
|
-
if (typeof v === 'number') return v.toLocaleString()
|
|
127
|
-
return String(v)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function StatDescription({ stat }: { stat: StatMeta }) {
|
|
131
|
-
const icon = stat.descriptionIcon
|
|
132
|
-
const before = icon?.position === 'before'
|
|
133
|
-
const text = stat.description
|
|
134
|
-
return (
|
|
135
|
-
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
136
|
-
{icon && before && <StatIcon name={icon.name} className="size-3.5" />}
|
|
137
|
-
{text && <span>{text}</span>}
|
|
138
|
-
{icon && !before && <StatIcon name={icon.name} className="size-3.5" />}
|
|
139
|
-
</div>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function StatIcon({ name, className }: { name: string; className: string }) {
|
|
144
|
-
// Stats ship icons as registry-name strings; mirror Resource.icon /
|
|
145
|
-
// panel chrome lookup so the same registry serves both. The lookup
|
|
146
|
-
// accepts a `SerializedIcon` shape — wrap the bare string.
|
|
147
|
-
const serialized: SerializedIcon = name
|
|
148
|
-
const Icon = useIconFor(serialized)
|
|
149
|
-
if (!Icon) return null
|
|
150
|
-
return <Icon className={className} aria-hidden />
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ─── Sparkline ────────────────────────────────────────────
|
|
154
|
-
|
|
155
|
-
interface SparklineProps {
|
|
156
|
-
values: number[]
|
|
157
|
-
className: string
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Inline-SVG sparkline. Sized fluidly via `viewBox` + `preserveAspectRatio`
|
|
162
|
-
* — the parent card sets the rendered height. No deps, no chart lib.
|
|
163
|
-
*
|
|
164
|
-
* Single-value series renders a flat horizontal line at mid-height; flat
|
|
165
|
-
* series (all values equal) likewise — avoids divide-by-zero when the
|
|
166
|
-
* range collapses.
|
|
167
|
-
*/
|
|
168
|
-
function Sparkline({ values, className }: SparklineProps) {
|
|
169
|
-
if (values.length === 0) return null
|
|
170
|
-
const W = 100
|
|
171
|
-
const H = 30
|
|
172
|
-
const min = Math.min(...values)
|
|
173
|
-
const max = Math.max(...values)
|
|
174
|
-
const range = max - min || 1
|
|
175
|
-
const stepX = values.length > 1 ? W / (values.length - 1) : 0
|
|
176
|
-
const points = values.map((v, i) => {
|
|
177
|
-
const x = stepX === 0 ? W / 2 : i * stepX
|
|
178
|
-
const y = H - ((v - min) / range) * H
|
|
179
|
-
return `${x.toFixed(2)},${y.toFixed(2)}`
|
|
180
|
-
}).join(' ')
|
|
181
|
-
return (
|
|
182
|
-
<svg
|
|
183
|
-
viewBox={`0 0 ${W} ${H}`}
|
|
184
|
-
preserveAspectRatio="none"
|
|
185
|
-
className={`mt-2 h-8 w-full ${className}`}
|
|
186
|
-
aria-hidden
|
|
187
|
-
>
|
|
188
|
-
<polyline
|
|
189
|
-
fill="none"
|
|
190
|
-
stroke="currentColor"
|
|
191
|
-
strokeWidth="1.5"
|
|
192
|
-
strokeLinecap="round"
|
|
193
|
-
strokeLinejoin="round"
|
|
194
|
-
points={points}
|
|
195
|
-
/>
|
|
196
|
-
</svg>
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// ─── Skeleton + error ─────────────────────────────────────
|
|
201
|
-
|
|
202
|
-
function StatsSkeleton({ columns }: { columns: number }) {
|
|
203
|
-
return (
|
|
204
|
-
<div
|
|
205
|
-
className="grid gap-4"
|
|
206
|
-
style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}
|
|
207
|
-
>
|
|
208
|
-
{Array.from({ length: columns }, (_, i) => (
|
|
209
|
-
<div key={i} className="rounded-xl border border-border bg-card p-5 shadow-sm">
|
|
210
|
-
<div className="flex flex-col gap-3">
|
|
211
|
-
<div className="h-4 w-24 rounded bg-muted animate-pulse" />
|
|
212
|
-
<div className="h-8 w-20 rounded bg-muted animate-pulse" />
|
|
213
|
-
<div className="h-3 w-32 rounded bg-muted animate-pulse" />
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
))}
|
|
217
|
-
</div>
|
|
218
|
-
)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function StatsError({ message, columns }: { message: string; columns: number }) {
|
|
222
|
-
return (
|
|
223
|
-
<div
|
|
224
|
-
className="grid gap-4"
|
|
225
|
-
style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}
|
|
226
|
-
>
|
|
227
|
-
<div className="rounded-xl border border-red-500/40 bg-red-50 p-5 text-sm text-red-700 dark:bg-red-950/30 dark:text-red-300">
|
|
228
|
-
Failed to load stats: {message}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
)
|
|
232
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../schema/Element.js'
|
|
3
|
-
import type { ColumnMeta } from '../../Column.js'
|
|
4
|
-
import { useWidgetData } from '../WidgetDataContext.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Plan #15 Phase D — `TableWidgetRenderer`.
|
|
8
|
-
*
|
|
9
|
-
* Reads `_widgetData[id]` from the surrounding `WidgetDataProvider`
|
|
10
|
-
* (lazy widgets fetch on mount via `widgetUrl`), then paints a slim
|
|
11
|
-
* HTML table — column headers + body rows. No filters, no bulk actions,
|
|
12
|
-
* no pagination, no search. The optional `viewAllUrl` renders as a
|
|
13
|
-
* "View all →" link in the widget header.
|
|
14
|
-
*
|
|
15
|
-
* Cell formatting mirrors the full `TableRenderer` paint: per-row
|
|
16
|
-
* `_formatted[colName]` (server-side `formatStateUsing`) wins over
|
|
17
|
-
* built-in `format` specs (`dateTime / since / money / numeric / limit`).
|
|
18
|
-
* Column types beyond plain text (`badge / icon / image`) defer to a
|
|
19
|
-
* compact local switch — slim widgets typically use plain text columns,
|
|
20
|
-
* but the dispatch is here for parity.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
interface TableWidgetPayload {
|
|
24
|
-
rows: Record<string, unknown>[]
|
|
25
|
-
total?: number
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface TableWidgetRendererProps {
|
|
29
|
-
meta: ElementMeta
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function TableWidgetRenderer({ meta }: TableWidgetRendererProps) {
|
|
33
|
-
const label = typeof meta['label'] === 'string' ? (meta['label'] as string) : undefined
|
|
34
|
-
const viewAllUrl = typeof meta['viewAllUrl'] === 'string' ? (meta['viewAllUrl'] as string) : undefined
|
|
35
|
-
const columns = readColumns(meta['columns'])
|
|
36
|
-
|
|
37
|
-
const { data, error, isLoading } = useWidgetData(meta as Parameters<typeof useWidgetData>[0])
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div className="rounded-xl border border-border bg-card shadow-sm">
|
|
41
|
-
{(label || viewAllUrl) && (
|
|
42
|
-
<div className="flex items-center justify-between gap-3 border-b border-border px-4 py-3">
|
|
43
|
-
{label
|
|
44
|
-
? <h3 className="text-sm font-medium text-foreground">{label}</h3>
|
|
45
|
-
: <span />}
|
|
46
|
-
{viewAllUrl && (
|
|
47
|
-
<a
|
|
48
|
-
href={viewAllUrl}
|
|
49
|
-
className="text-xs font-medium text-primary hover:underline"
|
|
50
|
-
>
|
|
51
|
-
View all →
|
|
52
|
-
</a>
|
|
53
|
-
)}
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
<div className="overflow-x-auto">
|
|
57
|
-
{isLoading
|
|
58
|
-
? <TableSkeleton columns={columns} />
|
|
59
|
-
: error
|
|
60
|
-
? <TableError message={error} />
|
|
61
|
-
: <TableBody columns={columns} rows={readRows(data)} />}
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function readColumns(raw: unknown): ColumnMeta[] {
|
|
68
|
-
return Array.isArray(raw) ? (raw as ColumnMeta[]) : []
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function readRows(data: unknown): Record<string, unknown>[] {
|
|
72
|
-
if (!data || typeof data !== 'object') return []
|
|
73
|
-
const rows = (data as TableWidgetPayload).rows
|
|
74
|
-
return Array.isArray(rows) ? rows : []
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface TableBodyProps {
|
|
78
|
-
columns: ColumnMeta[]
|
|
79
|
-
rows: Record<string, unknown>[]
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function TableBody({ columns, rows }: TableBodyProps) {
|
|
83
|
-
if (columns.length === 0) {
|
|
84
|
-
return <p className="px-4 py-6 text-sm text-muted-foreground">No columns configured.</p>
|
|
85
|
-
}
|
|
86
|
-
if (rows.length === 0) {
|
|
87
|
-
return <p className="px-4 py-6 text-sm text-muted-foreground">No records to display.</p>
|
|
88
|
-
}
|
|
89
|
-
return (
|
|
90
|
-
<table className="w-full text-sm">
|
|
91
|
-
<thead className="border-b border-border bg-muted/30">
|
|
92
|
-
<tr>
|
|
93
|
-
{columns.map(col => (
|
|
94
|
-
<th
|
|
95
|
-
key={col.name}
|
|
96
|
-
className={`px-4 py-2 text-left font-medium text-muted-foreground ${alignClass(col.alignment)}`}
|
|
97
|
-
style={col.width ? { width: col.width } : undefined}
|
|
98
|
-
>
|
|
99
|
-
{col.label}
|
|
100
|
-
</th>
|
|
101
|
-
))}
|
|
102
|
-
</tr>
|
|
103
|
-
</thead>
|
|
104
|
-
<tbody>
|
|
105
|
-
{rows.map((row, i) => (
|
|
106
|
-
<tr key={i} className="border-b border-border last:border-0">
|
|
107
|
-
{columns.map(col => (
|
|
108
|
-
<td
|
|
109
|
-
key={col.name}
|
|
110
|
-
className={`px-4 py-2 ${alignClass(col.alignment)}`}
|
|
111
|
-
>
|
|
112
|
-
{renderCell(row[col.name], col, row)}
|
|
113
|
-
</td>
|
|
114
|
-
))}
|
|
115
|
-
</tr>
|
|
116
|
-
))}
|
|
117
|
-
</tbody>
|
|
118
|
-
</table>
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function alignClass(a?: string): string {
|
|
123
|
-
if (a === 'center') return 'text-center'
|
|
124
|
-
if (a === 'end') return 'text-right'
|
|
125
|
-
return 'text-left'
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Slim cell renderer — handles per-row `_formatted` overrides + the
|
|
129
|
-
* built-in `format` spec. Falls back to a plain stringification for
|
|
130
|
-
* unhandled `columnType`s; richer painting (badges, icons, images)
|
|
131
|
-
* belongs to the full `TableRenderer` for the Resource list page. */
|
|
132
|
-
function renderCell(value: unknown, col: ColumnMeta, row: Record<string, unknown>): React.ReactNode {
|
|
133
|
-
const fallback = col.default ?? '—'
|
|
134
|
-
const formatted = (row['_formatted'] as Record<string, string> | undefined)?.[col.name]
|
|
135
|
-
if (formatted !== undefined && formatted !== '') return formatted
|
|
136
|
-
|
|
137
|
-
if (value === null || value === undefined || value === '') {
|
|
138
|
-
return <span className="text-muted-foreground">{fallback}</span>
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const fmt = col.format
|
|
142
|
-
if (fmt) return applyFormat(value, fmt)
|
|
143
|
-
|
|
144
|
-
if (value instanceof Date) {
|
|
145
|
-
return value.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })
|
|
146
|
-
}
|
|
147
|
-
if (typeof value === 'boolean') return value ? 'Yes' : 'No'
|
|
148
|
-
if (typeof value === 'object') return JSON.stringify(value)
|
|
149
|
-
return String(value)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function applyFormat(value: unknown, fmt: NonNullable<ColumnMeta['format']>): string {
|
|
153
|
-
switch (fmt.kind) {
|
|
154
|
-
case 'dateTime': {
|
|
155
|
-
const d = value instanceof Date ? value : new Date(String(value))
|
|
156
|
-
if (Number.isNaN(d.getTime())) return String(value)
|
|
157
|
-
return d.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })
|
|
158
|
-
}
|
|
159
|
-
case 'since': {
|
|
160
|
-
const d = value instanceof Date ? value : new Date(String(value))
|
|
161
|
-
if (Number.isNaN(d.getTime())) return String(value)
|
|
162
|
-
const seconds = Math.round((Date.now() - d.getTime()) / 1000)
|
|
163
|
-
if (seconds < 60) return `${seconds}s ago`
|
|
164
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`
|
|
165
|
-
if (seconds < 86_400) return `${Math.floor(seconds / 3600)}h ago`
|
|
166
|
-
return `${Math.floor(seconds / 86_400)}d ago`
|
|
167
|
-
}
|
|
168
|
-
case 'money': {
|
|
169
|
-
const n = Number(value)
|
|
170
|
-
if (!Number.isFinite(n)) return String(value)
|
|
171
|
-
return new Intl.NumberFormat(fmt.locale, { style: 'currency', currency: fmt.currency }).format(n)
|
|
172
|
-
}
|
|
173
|
-
case 'numeric': {
|
|
174
|
-
const n = Number(value)
|
|
175
|
-
if (!Number.isFinite(n)) return String(value)
|
|
176
|
-
const decimals = fmt.decimals ?? 0
|
|
177
|
-
return new Intl.NumberFormat(fmt.locale, {
|
|
178
|
-
minimumFractionDigits: decimals,
|
|
179
|
-
maximumFractionDigits: decimals,
|
|
180
|
-
}).format(n)
|
|
181
|
-
}
|
|
182
|
-
case 'limit': {
|
|
183
|
-
const s = String(value)
|
|
184
|
-
return s.length > fmt.chars ? `${s.slice(0, fmt.chars)}…` : s
|
|
185
|
-
}
|
|
186
|
-
case 'words': {
|
|
187
|
-
const s = String(value).trim()
|
|
188
|
-
if (s.length === 0) return s
|
|
189
|
-
const tokens = s.split(/\s+/)
|
|
190
|
-
return tokens.length > fmt.words ? `${tokens.slice(0, fmt.words).join(' ')}…` : s
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ─── Skeleton + error ─────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
function TableSkeleton({ columns }: { columns: ColumnMeta[] }) {
|
|
198
|
-
const cols = Math.max(1, columns.length || 3)
|
|
199
|
-
return (
|
|
200
|
-
<table className="w-full text-sm">
|
|
201
|
-
<thead className="border-b border-border bg-muted/30">
|
|
202
|
-
<tr>
|
|
203
|
-
{Array.from({ length: cols }, (_, i) => (
|
|
204
|
-
<th key={i} className="px-4 py-2">
|
|
205
|
-
<div className="h-3 w-16 rounded bg-muted animate-pulse" />
|
|
206
|
-
</th>
|
|
207
|
-
))}
|
|
208
|
-
</tr>
|
|
209
|
-
</thead>
|
|
210
|
-
<tbody>
|
|
211
|
-
{Array.from({ length: 3 }, (_, r) => (
|
|
212
|
-
<tr key={r} className="border-b border-border last:border-0">
|
|
213
|
-
{Array.from({ length: cols }, (_, c) => (
|
|
214
|
-
<td key={c} className="px-4 py-3">
|
|
215
|
-
<div className="h-3 w-full max-w-[10rem] rounded bg-muted animate-pulse" />
|
|
216
|
-
</td>
|
|
217
|
-
))}
|
|
218
|
-
</tr>
|
|
219
|
-
))}
|
|
220
|
-
</tbody>
|
|
221
|
-
</table>
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function TableError({ message }: { message: string }) {
|
|
226
|
-
return (
|
|
227
|
-
<div className="border-t border-red-500/40 bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/30 dark:text-red-300">
|
|
228
|
-
Failed to load table: {message}
|
|
229
|
-
</div>
|
|
230
|
-
)
|
|
231
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../schema/Element.js'
|
|
3
|
-
import { useWidgetData } from '../WidgetDataContext.js'
|
|
4
|
-
import { getWidgetComponent } from '../../widgets/registry.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Plan #15 — `ViewRenderer`. Mounts the user-supplied React component
|
|
8
|
-
* registered under `meta.component` via `registerWidgetComponents()`,
|
|
9
|
-
* passing `data` resolved from `View.getData(ctx)` (or fetched lazily
|
|
10
|
-
* via the polling endpoint).
|
|
11
|
-
*
|
|
12
|
-
* Failure modes paint inline error panels rather than throwing — a
|
|
13
|
-
* misnamed `View.component('Foo')` in production should surface a clear
|
|
14
|
-
* pointer at the registration step, not a blank widget slot.
|
|
15
|
-
*/
|
|
16
|
-
export interface ViewRendererProps {
|
|
17
|
-
meta: ElementMeta
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function ViewRenderer({ meta }: ViewRendererProps) {
|
|
21
|
-
const componentName = String(meta['component'] ?? '')
|
|
22
|
-
const { data, error, isLoading } = useWidgetData(meta as Parameters<typeof useWidgetData>[0])
|
|
23
|
-
|
|
24
|
-
if (!componentName) {
|
|
25
|
-
return (
|
|
26
|
-
<ViewError>
|
|
27
|
-
View widget is missing its <code className="font-mono">component</code> name —
|
|
28
|
-
set <code className="font-mono">static componentName = '...'</code> on the
|
|
29
|
-
subclass or call <code className="font-mono">.component('...')</code> in the
|
|
30
|
-
fluent form.
|
|
31
|
-
</ViewError>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const Component = getWidgetComponent(componentName)
|
|
36
|
-
if (!Component) {
|
|
37
|
-
return (
|
|
38
|
-
<ViewError>
|
|
39
|
-
No component registered under name <code className="font-mono">{componentName}</code>.
|
|
40
|
-
Register it at app boot:
|
|
41
|
-
<pre className="mt-2 overflow-x-auto rounded bg-amber-100/60 p-2 text-xs dark:bg-amber-900/30">{`import { registerWidgetComponents } from '@pilotiq/pilotiq/widgets'\nregisterWidgetComponents({ ${componentName}: ${componentName} })`}</pre>
|
|
42
|
-
</ViewError>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (isLoading) return <ViewSkeleton />
|
|
47
|
-
if (error) return <ViewError>{error}</ViewError>
|
|
48
|
-
|
|
49
|
-
return <Component data={data} />
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function ViewSkeleton() {
|
|
53
|
-
return (
|
|
54
|
-
<div
|
|
55
|
-
role="status"
|
|
56
|
-
aria-label="Loading"
|
|
57
|
-
className="h-32 animate-pulse rounded-md border border-border bg-muted/40"
|
|
58
|
-
/>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function ViewError({ children }: { children: React.ReactNode }) {
|
|
63
|
-
return (
|
|
64
|
-
<div
|
|
65
|
-
role="alert"
|
|
66
|
-
className="rounded-md border border-amber-500/40 bg-amber-50 p-3 text-sm text-amber-800 dark:bg-amber-950/30 dark:text-amber-200"
|
|
67
|
-
>
|
|
68
|
-
{children}
|
|
69
|
-
</div>
|
|
70
|
-
)
|
|
71
|
-
}
|