@questpie/admin 1.1.1 → 3.0.0
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/README.md +256 -488
- package/dist/augmentation.d.mts +414 -0
- package/dist/client/blocks/block-renderer.d.mts +45 -0
- package/dist/client/blocks/block-renderer.mjs +105 -0
- package/dist/client/blocks/index.d.mts +2 -0
- package/dist/client/blocks/types.d.mts +68 -0
- package/dist/client/blocks/types.mjs +19 -0
- package/dist/client/builder/admin-types.d.mts +40 -0
- package/dist/client/builder/admin.d.mts +110 -0
- package/dist/client/builder/admin.mjs +151 -0
- package/dist/client/builder/field/field.d.mts +51 -0
- package/dist/client/builder/field/field.mjs +37 -0
- package/dist/client/builder/index.d.mts +31 -0
- package/dist/client/builder/page/page.d.mts +24 -0
- package/dist/client/builder/page/page.mjs +31 -0
- package/dist/client/builder/registry.d.mts +50 -0
- package/dist/client/builder/types/action-registry.mjs +276 -0
- package/dist/client/builder/types/action-types.d.mts +226 -0
- package/dist/client/builder/types/collection-types.mjs +10 -0
- package/dist/client/builder/types/common.d.mts +46 -0
- package/dist/client/builder/types/field-types.d.mts +592 -0
- package/dist/client/builder/types/field-types.mjs +18 -0
- package/dist/client/builder/types/ui-config.d.mts +158 -0
- package/dist/client/builder/types/widget-types.d.mts +562 -0
- package/dist/client/builder/validation.d.mts +33 -0
- package/dist/client/builder/validation.mjs +227 -0
- package/dist/client/builder/view/view.d.mts +49 -0
- package/dist/client/builder/view/view.mjs +27 -0
- package/dist/client/builder/widget/widget.d.mts +27 -0
- package/dist/client/builder/widget/widget.mjs +21 -0
- package/dist/client/components/actions/action-button.mjs +245 -0
- package/dist/client/components/actions/action-dialog.mjs +559 -0
- package/dist/client/components/actions/confirmation-dialog.mjs +201 -0
- package/dist/client/components/actions/header-actions.mjs +210 -0
- package/dist/client/components/admin-link.d.mts +66 -0
- package/dist/client/components/admin-link.mjs +171 -0
- package/dist/client/components/auth/auth-guard.d.mts +50 -0
- package/dist/client/components/auth/auth-guard.mjs +60 -0
- package/dist/client/components/auth/auth-loading.d.mts +35 -0
- package/dist/client/components/auth/auth-loading.mjs +55 -0
- package/dist/client/components/blocks/block-canvas.mjs +159 -0
- package/dist/client/components/blocks/block-editor-context.mjs +125 -0
- package/dist/client/components/blocks/block-editor-layout.mjs +199 -0
- package/dist/client/components/blocks/block-editor-provider.mjs +235 -0
- package/dist/client/components/blocks/block-fields-renderer.mjs +240 -0
- package/dist/client/components/blocks/block-insert-button.mjs +189 -0
- package/dist/client/components/blocks/block-item-menu.mjs +363 -0
- package/dist/client/components/blocks/block-item.mjs +434 -0
- package/dist/client/components/blocks/block-library-sidebar.mjs +285 -0
- package/dist/client/components/blocks/block-tree.mjs +103 -0
- package/dist/client/components/blocks/block-type-icon.mjs +95 -0
- package/dist/client/components/blocks/utils/tree-utils.mjs +185 -0
- package/dist/client/components/component-renderer.d.mts +114 -0
- package/dist/client/components/component-renderer.mjs +258 -0
- package/dist/client/components/error-boundary.mjs +144 -0
- package/dist/client/components/fields/array-field.mjs +506 -0
- package/dist/client/components/fields/asset-preview-field.mjs +159 -0
- package/dist/client/components/fields/blocks-field/blocks-field.mjs +209 -0
- package/dist/client/components/fields/boolean-field.mjs +77 -0
- package/dist/client/components/fields/date-field.mjs +76 -0
- package/dist/client/components/fields/datetime-field.mjs +74 -0
- package/dist/client/components/fields/email-field.mjs +67 -0
- package/dist/client/components/fields/field-utils.mjs +64 -0
- package/dist/client/components/fields/field-wrapper.mjs +124 -0
- package/dist/client/components/fields/json-field.mjs +461 -0
- package/dist/client/components/fields/locale-badge.mjs +25 -0
- package/dist/client/components/fields/number-field.mjs +73 -0
- package/dist/client/components/fields/object-array-field.mjs +707 -0
- package/dist/client/components/fields/object-field.mjs +686 -0
- package/dist/client/components/fields/relation/displays/cards-display.mjs +254 -0
- package/dist/client/components/fields/relation/displays/chips-display.mjs +173 -0
- package/dist/client/components/fields/relation/displays/grid-display.mjs +209 -0
- package/dist/client/components/fields/relation/displays/list-display.mjs +247 -0
- package/dist/client/components/fields/relation/displays/table-display.mjs +309 -0
- package/dist/client/components/fields/relation/displays/types.mjs +72 -0
- package/dist/client/components/fields/relation/relation-items-display.mjs +143 -0
- package/dist/client/components/fields/relation-field.mjs +161 -0
- package/dist/client/components/fields/relation-picker.mjs +296 -0
- package/dist/client/components/fields/relation-select.mjs +232 -0
- package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +105 -0
- package/dist/client/components/fields/rich-text-editor/extensions.mjs +144 -0
- package/dist/client/components/fields/rich-text-editor/image-popover.mjs +221 -0
- package/dist/client/components/fields/rich-text-editor/index.mjs +520 -0
- package/dist/client/components/fields/rich-text-editor/link-popover.mjs +85 -0
- package/dist/client/components/fields/rich-text-editor/presets.mjs +123 -0
- package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +188 -0
- package/dist/client/components/fields/rich-text-editor/table-controls.mjs +433 -0
- package/dist/client/components/fields/rich-text-editor/toolbar.mjs +541 -0
- package/dist/client/components/fields/rich-text-editor/types.mjs +30 -0
- package/dist/client/components/fields/rich-text-editor/utils.mjs +50 -0
- package/dist/client/components/fields/rich-text-field.mjs +64 -0
- package/dist/client/components/fields/select-field.mjs +88 -0
- package/dist/client/components/fields/text-field.mjs +72 -0
- package/dist/client/components/fields/textarea-field.mjs +71 -0
- package/dist/client/components/fields/time-field.mjs +67 -0
- package/dist/client/components/fields/upload-field.mjs +621 -0
- package/dist/client/components/filter-builder/columns-tab.mjs +325 -0
- package/dist/client/components/filter-builder/filter-builder-sheet.mjs +456 -0
- package/dist/client/components/filter-builder/filters-tab.mjs +663 -0
- package/dist/client/components/filter-builder/saved-views-tab.mjs +278 -0
- package/dist/client/components/history-sidebar.mjs +496 -0
- package/dist/client/components/layout/field-layout-renderer.mjs +301 -0
- package/dist/client/components/locale-switcher.mjs +251 -0
- package/dist/client/components/media/media-grid.mjs +314 -0
- package/dist/client/components/media/media-picker-dialog.mjs +563 -0
- package/dist/client/components/preview/live-preview-mode.mjs +503 -0
- package/dist/client/components/preview/preview-pane.mjs +388 -0
- package/dist/client/components/primitives/asset-preview.mjs +771 -0
- package/dist/client/components/primitives/checkbox-input.mjs +59 -0
- package/dist/client/components/primitives/date-input.mjs +461 -0
- package/dist/client/components/primitives/dropzone.mjs +368 -0
- package/dist/client/components/primitives/number-input.mjs +117 -0
- package/dist/client/components/primitives/select-multi.mjs +590 -0
- package/dist/client/components/primitives/select-single.mjs +473 -0
- package/dist/client/components/primitives/text-input.mjs +45 -0
- package/dist/client/components/primitives/textarea-input.mjs +43 -0
- package/dist/client/components/primitives/time-input.mjs +145 -0
- package/dist/client/components/primitives/toggle-input.mjs +51 -0
- package/dist/client/components/primitives/types.mjs +12 -0
- package/dist/client/components/sheets/resource-sheet.mjs +86 -0
- package/dist/client/components/ui/accordion.mjs +169 -0
- package/dist/client/components/ui/alert.mjs +85 -0
- package/dist/client/components/ui/badge.mjs +76 -0
- package/dist/client/components/ui/button.mjs +84 -0
- package/dist/client/components/ui/card.mjs +206 -0
- package/dist/client/components/ui/checkbox.mjs +82 -0
- package/dist/client/components/ui/command.mjs +239 -0
- package/dist/client/components/ui/dialog.mjs +296 -0
- package/dist/client/components/ui/drawer.mjs +278 -0
- package/dist/client/components/ui/dropdown-menu.mjs +315 -0
- package/dist/client/components/ui/empty-state.mjs +99 -0
- package/dist/client/components/ui/field.mjs +354 -0
- package/dist/client/components/ui/input-group.mjs +201 -0
- package/dist/client/components/ui/input.mjs +46 -0
- package/dist/client/components/ui/kbd.mjs +53 -0
- package/dist/client/components/ui/label.mjs +45 -0
- package/dist/client/components/ui/popover.mjs +176 -0
- package/dist/client/components/ui/responsive-dialog.mjs +308 -0
- package/dist/client/components/ui/search-input.mjs +139 -0
- package/dist/client/components/ui/select.mjs +325 -0
- package/dist/client/components/ui/separator.mjs +47 -0
- package/dist/client/components/ui/sheet.mjs +280 -0
- package/dist/client/components/ui/sidebar.mjs +774 -0
- package/dist/client/components/ui/skeleton.mjs +40 -0
- package/dist/client/components/ui/sonner.d.mts +13 -0
- package/dist/client/components/ui/sonner.mjs +85 -0
- package/dist/client/components/ui/spinner.mjs +52 -0
- package/dist/client/components/ui/switch.mjs +58 -0
- package/dist/client/components/ui/table.mjs +276 -0
- package/dist/client/components/ui/tabs.mjs +160 -0
- package/dist/client/components/ui/textarea.mjs +40 -0
- package/dist/client/components/ui/toolbar.mjs +136 -0
- package/dist/client/components/ui/tooltip.mjs +146 -0
- package/dist/client/components/widgets/chart-widget.mjs +582 -0
- package/dist/client/components/widgets/progress-widget.mjs +200 -0
- package/dist/client/components/widgets/quick-actions-widget.mjs +209 -0
- package/dist/client/components/widgets/recent-items-widget.mjs +196 -0
- package/dist/client/components/widgets/stats-widget.mjs +261 -0
- package/dist/client/components/widgets/table-widget.mjs +273 -0
- package/dist/client/components/widgets/timeline-widget.mjs +279 -0
- package/dist/client/components/widgets/value-widget.mjs +312 -0
- package/dist/client/components/widgets/widget-skeletons.mjs +427 -0
- package/dist/client/contexts/breadcrumb-context.mjs +60 -0
- package/dist/client/contexts/focus-context.d.mts +87 -0
- package/dist/client/contexts/focus-context.mjs +250 -0
- package/dist/client/hooks/typed-hooks.d.mts +110 -0
- package/dist/client/hooks/typed-hooks.mjs +888 -0
- package/dist/client/hooks/use-action.mjs +329 -0
- package/dist/client/hooks/use-admin-config.mjs +69 -0
- package/dist/client/hooks/use-admin-preferences.mjs +171 -0
- package/dist/client/hooks/use-admin-routes.mjs +130 -0
- package/dist/client/hooks/use-audit-history.mjs +157 -0
- package/dist/client/hooks/use-auth.d.mts +97 -0
- package/dist/{use-auth-BoLmWtmU.mjs → client/hooks/use-auth.mjs} +9 -10
- package/dist/client/hooks/use-collection-fields.mjs +71 -0
- package/dist/client/hooks/use-collection-meta.mjs +153 -0
- package/dist/client/hooks/use-collection-schema.mjs +90 -0
- package/dist/client/hooks/use-collection-validation.mjs +88 -0
- package/dist/client/hooks/use-collection.d.mts +96 -0
- package/dist/client/hooks/use-collection.mjs +673 -0
- package/dist/client/hooks/use-current-user.d.mts +60 -0
- package/dist/client/hooks/use-current-user.mjs +79 -0
- package/dist/client/hooks/use-field-hooks.mjs +199 -0
- package/dist/client/hooks/use-field-options.d.mts +57 -0
- package/dist/client/hooks/use-field-options.mjs +269 -0
- package/dist/client/hooks/use-global-fields.mjs +58 -0
- package/dist/client/hooks/use-global-meta.mjs +105 -0
- package/dist/client/hooks/use-global-schema.mjs +53 -0
- package/dist/client/hooks/use-global.d.mts +51 -0
- package/dist/client/hooks/use-global.mjs +284 -0
- package/dist/client/hooks/use-locks.mjs +452 -0
- package/dist/client/hooks/use-media-query.d.mts +22 -0
- package/dist/client/hooks/use-media-query.mjs +81 -0
- package/dist/client/hooks/use-prefill-params.mjs +47 -0
- package/dist/client/hooks/use-questpie-query-options.mjs +50 -0
- package/dist/client/hooks/use-reactive-fields.d.mts +78 -0
- package/dist/client/hooks/use-reactive-fields.mjs +201 -0
- package/dist/client/hooks/use-realtime-highlight.mjs +158 -0
- package/dist/client/hooks/use-saved-views.mjs +140 -0
- package/dist/client/hooks/use-search-param-toggle.d.mts +12 -0
- package/dist/client/hooks/use-search-param-toggle.mjs +115 -0
- package/dist/client/hooks/use-search.mjs +258 -0
- package/dist/client/hooks/use-server-actions.mjs +191 -0
- package/dist/client/hooks/use-server-validation.mjs +291 -0
- package/dist/client/hooks/use-server-widget-data.d.mts +25 -0
- package/dist/client/hooks/use-server-widget-data.mjs +65 -0
- package/dist/client/hooks/use-setup-status.d.mts +38 -0
- package/dist/client/hooks/use-setup-status.mjs +62 -0
- package/dist/client/hooks/use-sidebar-search-param.d.mts +9 -0
- package/dist/client/hooks/use-sidebar-search-param.mjs +104 -0
- package/dist/client/hooks/use-transition-stage.mjs +125 -0
- package/dist/client/hooks/use-upload-collection.mjs +31 -0
- package/dist/client/hooks/use-upload.mjs +209 -0
- package/dist/client/hooks/use-validation-error-map.mjs +57 -0
- package/dist/client/hooks/use-view-state.mjs +479 -0
- package/dist/client/i18n/hooks.mjs +284 -0
- package/dist/client/i18n/intl-cache.mjs +64 -0
- package/dist/client/i18n/messages.mjs +6 -0
- package/dist/client/i18n/simple.d.mts +21 -0
- package/dist/client/i18n/simple.mjs +156 -0
- package/dist/client/i18n/types.d.mts +109 -0
- package/dist/client/lib/cookies.mjs +9 -0
- package/dist/client/lib/events.mjs +5 -0
- package/dist/client/lib/render-profiler.mjs +51 -0
- package/dist/client/lib/utils.mjs +23 -0
- package/dist/client/preview/block-scope-context.d.mts +73 -0
- package/dist/client/preview/block-scope-context.mjs +116 -0
- package/dist/client/preview/index.d.mts +5 -0
- package/dist/client/preview/preview-banner.d.mts +45 -0
- package/dist/client/preview/preview-banner.mjs +134 -0
- package/dist/client/preview/preview-field.d.mts +107 -0
- package/dist/client/preview/preview-field.mjs +227 -0
- package/dist/client/preview/types.d.mts +114 -0
- package/dist/client/preview/types.mjs +20 -0
- package/dist/client/preview/use-collection-preview.d.mts +70 -0
- package/dist/client/preview/use-collection-preview.mjs +163 -0
- package/dist/client/runtime/content-locales-provider.d.mts +1 -0
- package/dist/client/runtime/content-locales-provider.mjs +136 -0
- package/dist/client/runtime/index.d.mts +5 -0
- package/dist/client/runtime/index.mjs +6 -0
- package/dist/client/runtime/locale-scope.d.mts +2 -0
- package/dist/client/runtime/locale-scope.mjs +108 -0
- package/dist/client/runtime/provider.d.mts +203 -0
- package/dist/client/runtime/provider.mjs +393 -0
- package/dist/client/runtime/routes.d.mts +49 -0
- package/dist/client/runtime/routes.mjs +91 -0
- package/dist/client/runtime/translations-provider.mjs +242 -0
- package/dist/client/scope/index.d.mts +3 -0
- package/dist/client/scope/picker.d.mts +53 -0
- package/dist/client/scope/picker.mjs +395 -0
- package/dist/client/scope/provider.d.mts +103 -0
- package/dist/client/scope/provider.mjs +167 -0
- package/dist/client/scope/types.d.mts +111 -0
- package/dist/client/styles/index.css +344 -188
- package/dist/client/utils/auto-expand-fields.mjs +83 -0
- package/dist/client/utils/build-field-definitions-from-schema.mjs +259 -0
- package/dist/client/utils/dependency-tracker.mjs +61 -0
- package/dist/client/utils/detect-relations.mjs +45 -0
- package/dist/client/utils/locale-to-flag.d.mts +119 -0
- package/dist/client/utils/locale-to-flag.mjs +129 -0
- package/dist/client/utils/routes.mjs +195 -0
- package/dist/client/views/auth/accept-invite-form.d.mts +72 -0
- package/dist/client/views/auth/accept-invite-form.mjs +473 -0
- package/dist/client/views/auth/auth-layout.d.mts +43 -0
- package/dist/client/views/auth/auth-layout.mjs +116 -0
- package/dist/client/views/auth/forgot-password-form.d.mts +58 -0
- package/dist/client/views/auth/forgot-password-form.mjs +386 -0
- package/dist/client/views/auth/invite-form.mjs +497 -0
- package/dist/client/views/auth/login-form.d.mts +75 -0
- package/dist/client/views/auth/login-form.mjs +387 -0
- package/dist/client/views/auth/reset-password-form.d.mts +65 -0
- package/dist/client/views/auth/reset-password-form.mjs +515 -0
- package/dist/client/views/auth/setup-form.d.mts +60 -0
- package/dist/client/views/auth/setup-form.mjs +526 -0
- package/dist/client/views/collection/auto-form-fields.mjs +873 -0
- package/dist/client/views/collection/bulk-action-toolbar.mjs +480 -0
- package/dist/client/views/collection/cells/complex-cells.mjs +746 -0
- package/dist/client/views/collection/cells/primitive-cells.mjs +395 -0
- package/dist/client/views/collection/cells/relation-cells.mjs +260 -0
- package/dist/client/views/collection/cells/shared/asset-thumbnail.mjs +509 -0
- package/dist/client/views/collection/cells/shared/cell-helpers.mjs +125 -0
- package/dist/client/views/collection/cells/shared/relation-chip.mjs +121 -0
- package/dist/client/views/collection/cells/upload-cells.mjs +28 -0
- package/dist/client/views/collection/columns/build-columns.mjs +195 -0
- package/dist/client/views/collection/columns/column-defaults.mjs +123 -0
- package/dist/client/views/collection/field-context.mjs +126 -0
- package/dist/client/views/collection/field-renderer.mjs +368 -0
- package/dist/client/views/collection/form-view.mjs +1566 -0
- package/dist/client/views/collection/table-view.mjs +832 -0
- package/dist/client/views/collection/view-skeletons.mjs +215 -0
- package/dist/client/views/common/global-search.mjs +700 -0
- package/dist/client/views/dashboard/dashboard-grid.mjs +881 -0
- package/dist/client/views/dashboard/dashboard-widget.mjs +265 -0
- package/dist/client/views/dashboard/widget-card.mjs +372 -0
- package/dist/client/views/globals/global-form-view.mjs +1306 -0
- package/dist/client/views/layout/admin-layout-provider.d.mts +137 -0
- package/dist/client/views/layout/admin-layout-provider.mjs +193 -0
- package/dist/client/views/layout/admin-layout.d.mts +133 -0
- package/dist/client/views/layout/admin-layout.mjs +302 -0
- package/dist/client/views/layout/admin-router.d.mts +93 -0
- package/dist/client/views/layout/admin-router.mjs +1254 -0
- package/dist/client/views/layout/admin-sidebar.d.mts +99 -0
- package/dist/client/views/layout/admin-sidebar.mjs +1379 -0
- package/dist/client/views/layout/admin-topbar.mjs +236 -0
- package/dist/client/views/pages/accept-invite-page.d.mts +66 -0
- package/dist/client/views/pages/accept-invite-page.mjs +349 -0
- package/dist/client/views/pages/dashboard-page.d.mts +43 -0
- package/dist/client/views/pages/dashboard-page.mjs +84 -0
- package/dist/client/views/pages/forgot-password-page.d.mts +56 -0
- package/dist/client/views/pages/forgot-password-page.mjs +152 -0
- package/dist/client/views/pages/invite-page.d.mts +70 -0
- package/dist/client/views/pages/invite-page.mjs +161 -0
- package/dist/client/views/pages/login-page.d.mts +69 -0
- package/dist/client/views/pages/login-page.mjs +193 -0
- package/dist/client/views/pages/reset-password-page.d.mts +63 -0
- package/dist/client/views/pages/reset-password-page.mjs +257 -0
- package/dist/client/views/pages/setup-page.d.mts +61 -0
- package/dist/client/views/pages/setup-page.mjs +156 -0
- package/dist/client-module.d.mts +3 -0
- package/dist/client-module.mjs +3 -0
- package/dist/client.d.mts +99 -3
- package/dist/client.mjs +52 -12
- package/dist/components/rich-text/index.d.mts +1 -0
- package/dist/components/rich-text/rich-text-renderer.d.mts +103 -0
- package/dist/components/rich-text/rich-text-renderer.mjs +237 -0
- package/dist/index.d.mts +59 -3
- package/dist/index.mjs +52 -12
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.mjs +3 -0
- package/dist/server/adapters/nextjs.d.mts +120 -0
- package/dist/server/adapters/nextjs.mjs +110 -0
- package/dist/server/adapters/tanstack.d.mts +132 -0
- package/dist/server/adapters/tanstack.mjs +89 -0
- package/dist/server/augmentation/actions.d.mts +279 -0
- package/dist/server/augmentation/common.d.mts +76 -0
- package/dist/server/augmentation/dashboard.d.mts +547 -0
- package/dist/server/augmentation/form-layout.d.mts +303 -0
- package/dist/server/augmentation/index.d.mts +44 -0
- package/dist/server/augmentation/index.mjs +10 -0
- package/dist/server/augmentation/sidebar.d.mts +181 -0
- package/dist/server/augmentation/views.d.mts +237 -0
- package/dist/server/augmentation.d.mts +7 -0
- package/dist/server/block/index.d.mts +4 -0
- package/dist/server/codegen/admin-client-template.mjs +93 -0
- package/dist/server/codegen/projection-validator.mjs +67 -0
- package/dist/server/fields/blocks.d.mts +71 -0
- package/dist/server/fields/blocks.mjs +151 -0
- package/dist/server/fields/index.d.mts +15 -0
- package/dist/server/fields/index.mjs +15 -0
- package/dist/server/fields/rich-text.d.mts +68 -0
- package/dist/server/fields/rich-text.mjs +147 -0
- package/dist/server/i18n/index.mjs +43 -0
- package/dist/server/i18n/messages/cs.mjs +461 -0
- package/dist/server/i18n/messages/de.mjs +461 -0
- package/dist/server/i18n/messages/en.mjs +707 -0
- package/dist/server/i18n/messages/es.mjs +461 -0
- package/dist/server/i18n/messages/fr.mjs +461 -0
- package/dist/server/i18n/messages/index.mjs +23 -0
- package/dist/server/i18n/messages/pl.mjs +461 -0
- package/dist/server/i18n/messages/pt.mjs +464 -0
- package/dist/server/i18n/messages/sk.mjs +689 -0
- package/dist/server/modules/admin/.generated/module.d.mts +52 -0
- package/dist/server/modules/admin/.generated/module.mjs +78 -0
- package/dist/server/modules/admin/.generated/registries.d.mts +22 -0
- package/dist/server/modules/admin/auth-helpers.d.mts +142 -0
- package/dist/server/modules/admin/auth-helpers.mjs +107 -0
- package/dist/server/modules/admin/block/block-builder.d.mts +383 -0
- package/dist/server/modules/admin/block/block-builder.mjs +315 -0
- package/dist/server/modules/admin/block/index.d.mts +3 -0
- package/dist/server/modules/admin/block/introspection.d.mts +89 -0
- package/dist/server/modules/admin/block/introspection.mjs +100 -0
- package/dist/server/modules/admin/block/prefetch.d.mts +78 -0
- package/dist/server/modules/admin/block/prefetch.mjs +241 -0
- package/dist/server/modules/admin/client/.generated/module.d.mts +94 -0
- package/dist/server/modules/admin/client/.generated/module.mjs +91 -0
- package/dist/server/modules/admin/client/components/badge.d.mts +1 -0
- package/dist/server/modules/admin/client/components/icon.d.mts +1 -0
- package/dist/server/modules/admin/client/fields/array.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/array.mjs +12 -0
- package/dist/server/modules/admin/client/fields/assetPreview.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/assetPreview.mjs +18 -0
- package/dist/server/modules/admin/client/fields/blocks.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/blocks.mjs +12 -0
- package/dist/server/modules/admin/client/fields/boolean.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/boolean.mjs +12 -0
- package/dist/server/modules/admin/client/fields/date.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/date.mjs +12 -0
- package/dist/server/modules/admin/client/fields/datetime.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/datetime.mjs +12 -0
- package/dist/server/modules/admin/client/fields/email.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/email.mjs +12 -0
- package/dist/server/modules/admin/client/fields/json.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/json.mjs +11 -0
- package/dist/server/modules/admin/client/fields/number.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/number.mjs +12 -0
- package/dist/server/modules/admin/client/fields/object.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/object.mjs +12 -0
- package/dist/server/modules/admin/client/fields/relation.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/relation.mjs +12 -0
- package/dist/server/modules/admin/client/fields/richText.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/richText.mjs +11 -0
- package/dist/server/modules/admin/client/fields/select.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/select.mjs +12 -0
- package/dist/server/modules/admin/client/fields/text.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/text.mjs +12 -0
- package/dist/server/modules/admin/client/fields/textarea.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/textarea.mjs +12 -0
- package/dist/server/modules/admin/client/fields/time.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/time.mjs +12 -0
- package/dist/server/modules/admin/client/fields/upload.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/upload.mjs +12 -0
- package/dist/server/modules/admin/client/fields/url.d.mts +6 -0
- package/dist/server/modules/admin/client/fields/url.mjs +12 -0
- package/dist/server/modules/admin/client/index.d.mts +1 -0
- package/dist/server/modules/admin/client/pages/dashboard.d.mts +6 -0
- package/dist/server/modules/admin/client/pages/dashboard.mjs +11 -0
- package/dist/server/modules/admin/client/pages/forgotPassword.d.mts +6 -0
- package/dist/server/modules/admin/client/pages/forgotPassword.mjs +11 -0
- package/dist/server/modules/admin/client/pages/login.d.mts +6 -0
- package/dist/server/modules/admin/client/pages/login.mjs +11 -0
- package/dist/server/modules/admin/client/pages/resetPassword.d.mts +6 -0
- package/dist/server/modules/admin/client/pages/resetPassword.mjs +11 -0
- package/dist/server/modules/admin/client/pages/setup.d.mts +6 -0
- package/dist/server/modules/admin/client/pages/setup.mjs +11 -0
- package/dist/server/modules/admin/client/views/collection-form.mjs +10 -0
- package/dist/server/modules/admin/client/views/collection-table.mjs +10 -0
- package/dist/server/modules/admin/client/views/global-form.mjs +10 -0
- package/dist/server/modules/admin/client/widgets/chart.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/chart.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/progress.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/progress.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/quickActions.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/quickActions.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/recentItems.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/recentItems.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/stats.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/stats.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/table.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/table.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/timeline.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/timeline.mjs +7 -0
- package/dist/server/modules/admin/client/widgets/value.d.mts +6 -0
- package/dist/server/modules/admin/client/widgets/value.mjs +7 -0
- package/dist/server/modules/admin/collections/account.d.mts +77 -0
- package/dist/server/modules/admin/collections/account.mjs +23 -0
- package/dist/server/modules/admin/collections/admin-locks.d.mts +90 -0
- package/dist/server/modules/admin/collections/admin-locks.mjs +16 -0
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +69 -0
- package/dist/server/modules/admin/collections/admin-preferences.mjs +16 -0
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +83 -0
- package/dist/server/modules/admin/collections/admin-saved-views.mjs +16 -0
- package/dist/server/modules/admin/collections/apikey.d.mts +99 -0
- package/dist/server/modules/admin/collections/apikey.mjs +15 -0
- package/dist/server/modules/admin/collections/assets.d.mts +72 -0
- package/dist/server/modules/admin/collections/assets.mjs +71 -0
- package/dist/server/modules/admin/collections/session.d.mts +69 -0
- package/dist/server/modules/admin/collections/session.mjs +15 -0
- package/dist/server/modules/admin/collections/user.d.mts +85 -0
- package/dist/server/modules/admin/collections/user.mjs +204 -0
- package/dist/server/modules/admin/collections/verification.d.mts +63 -0
- package/dist/server/modules/admin/collections/verification.mjs +15 -0
- package/dist/server/modules/admin/components/badge.d.mts +10 -0
- package/dist/server/modules/admin/components/badge.mjs +10 -0
- package/dist/server/modules/admin/components/icon.d.mts +9 -0
- package/dist/server/modules/admin/components/icon.mjs +11 -0
- package/dist/server/modules/admin/config/admin.mjs +34 -0
- package/dist/server/modules/admin/dto/admin-config.dto.mjs +102 -0
- package/dist/server/modules/admin/factories.mjs +172 -0
- package/dist/server/modules/admin/index.d.mts +1093 -0
- package/dist/server/modules/admin/index.mjs +26 -0
- package/dist/server/modules/admin/modules.mjs +11 -0
- package/dist/server/modules/admin/plugin.mjs +13 -0
- package/dist/server/modules/admin/routes/admin-config.d.mts +28 -0
- package/dist/server/modules/admin/routes/admin-config.mjs +552 -0
- package/dist/server/modules/admin/routes/execute-action.d.mts +92 -0
- package/dist/server/modules/admin/routes/execute-action.mjs +433 -0
- package/dist/server/modules/admin/routes/locales.d.mts +18 -0
- package/dist/server/modules/admin/routes/locales.mjs +69 -0
- package/dist/server/modules/admin/routes/preview.d.mts +80 -0
- package/dist/server/modules/admin/routes/preview.mjs +230 -0
- package/dist/server/modules/admin/routes/reactive.d.mts +74 -0
- package/dist/server/modules/admin/routes/reactive.mjs +350 -0
- package/dist/server/modules/admin/routes/route-helpers.d.mts +23 -0
- package/dist/server/modules/admin/routes/route-helpers.mjs +76 -0
- package/dist/server/modules/admin/routes/setup.d.mts +63 -0
- package/dist/server/modules/admin/routes/setup.mjs +114 -0
- package/dist/server/modules/admin/routes/translations.d.mts +26 -0
- package/dist/server/modules/admin/routes/translations.mjs +114 -0
- package/dist/server/modules/admin/routes/widget-data.d.mts +32 -0
- package/dist/server/modules/admin/routes/widget-data.mjs +62 -0
- package/dist/server/modules/admin/views/form.d.mts +8 -0
- package/dist/server/modules/admin/views/form.mjs +7 -0
- package/dist/server/modules/admin/views/global-form.d.mts +8 -0
- package/dist/server/modules/admin/views/global-form.mjs +7 -0
- package/dist/server/modules/admin/views/table.d.mts +8 -0
- package/dist/server/modules/admin/views/table.mjs +7 -0
- package/dist/server/modules/admin-preferences/collections/admin-preferences.mjs +37 -0
- package/dist/server/modules/admin-preferences/collections/locks.collection.mjs +55 -0
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +99 -0
- package/dist/server/modules/admin-preferences/collections/saved-views.mjs +38 -0
- package/dist/server/modules/audit/.generated/module.d.mts +60 -0
- package/dist/server/modules/audit/.generated/module.mjs +30 -0
- package/dist/server/modules/audit/collections/audit-log.d.mts +214 -0
- package/dist/server/modules/audit/collections/audit-log.mjs +107 -0
- package/dist/server/modules/audit/config/admin.mjs +21 -0
- package/dist/server/modules/audit/config/app.mjs +262 -0
- package/dist/server/modules/audit/index.d.mts +2 -0
- package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +13 -0
- package/dist/server/modules/audit/jobs/audit-cleanup.mjs +28 -0
- package/dist/server/plugin.d.mts +26 -0
- package/dist/server/plugin.mjs +385 -0
- package/dist/server/proxy-factories.d.mts +85 -0
- package/dist/server/proxy-factories.mjs +302 -0
- package/dist/server/registry-helpers.d.mts +83 -0
- package/dist/server/registry-helpers.mjs +104 -0
- package/dist/server.d.mts +33 -250
- package/dist/server.mjs +22 -832
- package/dist/shared/preview-utils.d.mts +53 -0
- package/dist/{preview-utils-BKQ9-TMa.mjs → shared/preview-utils.mjs} +2 -3
- package/dist/{saved-views.types-BMsz5mCy.d.mts → shared/types/saved-views.types.d.mts} +7 -2
- package/dist/shared.d.mts +3 -57
- package/dist/shared.mjs +1 -1
- package/package.json +56 -51
- package/skills/questpie-admin/SKILL.md +397 -0
- package/skills/questpie-admin/blocks/SKILL.md +305 -0
- package/skills/questpie-admin/custom-ui/SKILL.md +307 -0
- package/skills/questpie-admin/views/SKILL.md +442 -0
- package/dist/auth-layout-M8K8_q5R.mjs +0 -181
- package/dist/auth-layout-M8K8_q5R.mjs.map +0 -1
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs +0 -273
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs.map +0 -1
- package/dist/card-BKHjBQfw.mjs +0 -58
- package/dist/card-BKHjBQfw.mjs.map +0 -1
- package/dist/client-D1DqawtP.d.mts +0 -13403
- package/dist/client-D1DqawtP.d.mts.map +0 -1
- package/dist/client-njX1rZmi.mjs +0 -22612
- package/dist/client-njX1rZmi.mjs.map +0 -1
- package/dist/content-locales-provider-BXvuIgfg.mjs +0 -1650
- package/dist/content-locales-provider-BXvuIgfg.mjs.map +0 -1
- package/dist/dashboard-page-B4PGEdc2.mjs +0 -2500
- package/dist/dashboard-page-B4PGEdc2.mjs.map +0 -1
- package/dist/dashboard-page-mCY0pgZv.mjs +0 -3
- package/dist/dropzone-Do3awXKd.mjs +0 -634
- package/dist/dropzone-Do3awXKd.mjs.map +0 -1
- package/dist/forgot-password-page-Bcp-An4Y.mjs +0 -221
- package/dist/forgot-password-page-Bcp-An4Y.mjs.map +0 -1
- package/dist/forgot-password-page-CEwsdLwn.mjs +0 -3
- package/dist/index-B4H3amCD.d.mts +0 -2753
- package/dist/index-B4H3amCD.d.mts.map +0 -1
- package/dist/login-page-BUnpCbCa.mjs +0 -3
- package/dist/login-page-CP4gA-dl.mjs +0 -298
- package/dist/login-page-CP4gA-dl.mjs.map +0 -1
- package/dist/preview-utils-BKQ9-TMa.mjs.map +0 -1
- package/dist/reset-password-page-BqfDmLxA.mjs +0 -281
- package/dist/reset-password-page-BqfDmLxA.mjs.map +0 -1
- package/dist/reset-password-page-CufHz3h3.mjs +0 -3
- package/dist/runtime-6VZM878K.mjs +0 -69
- package/dist/runtime-6VZM878K.mjs.map +0 -1
- package/dist/saved-views.types-BMsz5mCy.d.mts.map +0 -1
- package/dist/server.d.mts.map +0 -1
- package/dist/server.mjs.map +0 -1
- package/dist/setup-page-BNNzt_Z6.mjs +0 -3
- package/dist/setup-page-YAP_fzqh.mjs +0 -264
- package/dist/setup-page-YAP_fzqh.mjs.map +0 -1
- package/dist/shared.d.mts.map +0 -1
- package/dist/use-auth-BoLmWtmU.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reset-password-page-BqfDmLxA.mjs","names":["React","React"],"sources":["../src/client/views/auth/reset-password-form.tsx","../src/client/views/pages/reset-password-page.tsx"],"sourcesContent":["/**\n * Reset Password Form - set new password with token\n */\n\nimport {\n CheckCircle,\n Lock,\n SpinnerGap,\n WarningCircle,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { Alert, AlertDescription } from \"../../components/ui/alert\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"../../components/ui/field\";\nimport { Input } from \"../../components/ui/input\";\nimport { cn } from \"../../lib/utils\";\n\nexport type ResetPasswordFormValues = {\n password: string;\n confirmPassword: string;\n};\n\nexport type ResetPasswordFormProps = {\n /** Reset token from URL */\n token: string;\n /** Called when form is submitted with valid data */\n onSubmit: (\n values: ResetPasswordFormValues & { token: string },\n ) => Promise<void>;\n /** Called when back to login link is clicked */\n onBackToLoginClick?: () => void;\n /** Minimum password length */\n minPasswordLength?: number;\n /** Additional class name */\n className?: string;\n /** Error message from auth */\n error?: string | null;\n};\n\n/**\n * Reset password form with password confirmation\n *\n * @example\n * ```tsx\n * const authClient = createAdminAuthClient<typeof cms>({ baseURL: '...' })\n *\n * function ResetPasswordPage() {\n * const token = useSearchParams().get('token')\n * const [error, setError] = useState<string | null>(null)\n *\n * const handleSubmit = async (values) => {\n * const result = await authClient.resetPassword({\n * token: values.token,\n * newPassword: values.password,\n * })\n * if (result.error) {\n * setError(result.error.message)\n * }\n * }\n *\n * return (\n * <AuthLayout title=\"Reset password\">\n * <ResetPasswordForm token={token} onSubmit={handleSubmit} error={error} />\n * </AuthLayout>\n * )\n * }\n * ```\n */\nexport function ResetPasswordForm({\n token,\n onSubmit,\n onBackToLoginClick,\n minPasswordLength = 8,\n className,\n error,\n}: ResetPasswordFormProps) {\n const [isSuccess, setIsSuccess] = React.useState(false);\n\n const {\n register,\n handleSubmit,\n watch,\n formState: { errors, isSubmitting },\n } = useForm<ResetPasswordFormValues>({\n defaultValues: {\n password: \"\",\n confirmPassword: \"\",\n },\n });\n\n const password = watch(\"password\");\n\n const handleFormSubmit = handleSubmit(async (values) => {\n await onSubmit({ ...values, token });\n if (!error) {\n setIsSuccess(true);\n }\n });\n\n // Success state\n if (isSuccess) {\n return (\n <div className={cn(\"space-y-4 text-center\", className)}>\n <div className=\"bg-primary/10 mx-auto flex size-12 items-center justify-center\">\n <CheckCircle className=\"text-primary size-6\" weight=\"duotone\" />\n </div>\n <div className=\"space-y-2\">\n <h3 className=\"text-sm font-medium\">Password reset successful</h3>\n <p className=\"text-muted-foreground text-xs\">\n Your password has been reset successfully. You can now sign in with\n your new password.\n </p>\n </div>\n <Button\n type=\"button\"\n className=\"w-full\"\n size=\"lg\"\n onClick={onBackToLoginClick}\n >\n Sign in\n </Button>\n </div>\n );\n }\n\n return (\n <form onSubmit={handleFormSubmit} className={cn(\"space-y-4\", className)}>\n <p className=\"text-muted-foreground text-xs\">\n Enter your new password below.\n </p>\n\n <FieldGroup>\n {/* Password Field */}\n <Field data-invalid={!!errors.password}>\n <FieldLabel htmlFor=\"password\">New password</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <Lock\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"password\"\n type=\"password\"\n placeholder=\"Enter new password\"\n className=\"pl-8\"\n autoComplete=\"new-password\"\n aria-invalid={!!errors.password}\n {...register(\"password\", {\n required: \"Password is required\",\n minLength: {\n value: minPasswordLength,\n message: `Password must be at least ${minPasswordLength} characters`,\n },\n })}\n />\n </div>\n <FieldDescription>\n Must be at least {minPasswordLength} characters\n </FieldDescription>\n <FieldError>{errors.password?.message}</FieldError>\n </FieldContent>\n </Field>\n\n {/* Confirm Password Field */}\n <Field data-invalid={!!errors.confirmPassword}>\n <FieldLabel htmlFor=\"confirmPassword\">Confirm password</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <Lock\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"confirmPassword\"\n type=\"password\"\n placeholder=\"Confirm new password\"\n className=\"pl-8\"\n autoComplete=\"new-password\"\n aria-invalid={!!errors.confirmPassword}\n {...register(\"confirmPassword\", {\n required: \"Please confirm your password\",\n validate: (value) =>\n value === password || \"Passwords do not match\",\n })}\n />\n </div>\n <FieldError>{errors.confirmPassword?.message}</FieldError>\n </FieldContent>\n </Field>\n </FieldGroup>\n\n {/* Error Message */}\n {error && (\n <Alert variant=\"destructive\">\n <WarningCircle />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n\n {/* Submit Button */}\n <Button\n type=\"submit\"\n className=\"w-full\"\n size=\"lg\"\n disabled={isSubmitting}\n >\n {isSubmitting ? (\n <>\n <SpinnerGap className=\"animate-spin\" weight=\"bold\" />\n Resetting...\n </>\n ) : (\n \"Reset password\"\n )}\n </Button>\n\n {/* Back to Login Link */}\n <p className=\"text-muted-foreground text-center text-xs\">\n Remember your password?{\" \"}\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n onClick={onBackToLoginClick}\n className=\"h-auto p-0 text-xs\"\n >\n Back to login\n </Button>\n </p>\n </form>\n );\n}\n","/**\n * Reset Password Page\n *\n * Default reset password page that uses AuthLayout and ResetPasswordForm.\n * Integrates with authClient from AdminProvider context.\n */\n\nimport * as React from \"react\";\nimport { Button } from \"../../components/ui/button\";\nimport { useAuthClient } from \"../../hooks/use-auth\";\nimport {\n selectBasePath,\n selectBrandName,\n selectNavigate,\n useAdminStore,\n} from \"../../runtime/provider\";\nimport { AuthLayout } from \"../auth/auth-layout\";\nimport {\n ResetPasswordForm,\n type ResetPasswordFormValues,\n} from \"../auth/reset-password-form\";\n\nexport interface ResetPasswordPageProps {\n /**\n * Title shown on the page\n * @default \"Reset password\"\n */\n title?: string;\n\n /**\n * Description shown below the title\n * @default \"Enter your new password\"\n */\n description?: string;\n\n /**\n * Logo component to show above the form\n */\n logo?: React.ReactNode;\n\n /**\n * Path to login page\n * @default \"{basePath}/login\"\n */\n loginPath?: string;\n\n /**\n * Minimum password length\n * @default 8\n */\n minPasswordLength?: number;\n\n /**\n * Function to get token from URL.\n * By default, reads from URL search params (?token=...)\n */\n getToken?: () => string | null;\n}\n\n/**\n * Default reset password page component.\n *\n * Uses authClient from AdminProvider to handle password reset.\n * Expects a token in the URL query params (?token=...).\n *\n * @example\n * ```tsx\n * // In your admin config\n * const admin = qa<AppCMS>()\n * .use(coreAdminModule)\n * .pages({\n * resetPassword: page(\"reset-password\", { component: ResetPasswordPage })\n * .path(\"/reset-password\"),\n * })\n * ```\n */\nexport function ResetPasswordPage({\n title = \"Reset password\",\n description = \"Enter your new password\",\n logo,\n loginPath,\n minPasswordLength = 8,\n getToken,\n}: ResetPasswordPageProps) {\n const authClient = useAuthClient();\n const navigate = useAdminStore(selectNavigate);\n const basePath = useAdminStore(selectBasePath);\n const brandName = useAdminStore(selectBrandName);\n\n const [error, setError] = React.useState<string | null>(null);\n\n // Get token from URL\n const token = React.useMemo(() => {\n if (getToken) {\n return getToken();\n }\n if (typeof window !== \"undefined\") {\n const params = new URLSearchParams(window.location.search);\n return params.get(\"token\");\n }\n return null;\n }, [getToken]);\n\n const handleSubmit = async (\n values: ResetPasswordFormValues & { token: string },\n ) => {\n setError(null);\n\n try {\n const result = await authClient.resetPassword({\n token: values.token,\n newPassword: values.password,\n });\n\n if (result.error) {\n setError(result.error.message || \"Failed to reset password\");\n return;\n }\n\n // Success is handled by the form (shows success message)\n } catch (err) {\n setError(err instanceof Error ? err.message : \"An error occurred\");\n }\n };\n\n const handleBackToLoginClick = () => {\n navigate(loginPath ?? `${basePath}/login`);\n };\n\n // Show error if no token\n if (!token) {\n return (\n <AuthLayout\n title=\"Invalid Link\"\n description=\"The password reset link is invalid or has expired.\"\n logo={logo ?? <DefaultLogo brandName={brandName} />}\n >\n <div className=\"space-y-4 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n Please request a new password reset link.\n </p>\n <Button type=\"button\" variant=\"link\" onClick={handleBackToLoginClick}>\n Back to login\n </Button>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={title}\n description={description}\n logo={logo ?? <DefaultLogo brandName={brandName} />}\n >\n <ResetPasswordForm\n token={token}\n onSubmit={handleSubmit}\n onBackToLoginClick={handleBackToLoginClick}\n minPasswordLength={minPasswordLength}\n error={error}\n />\n </AuthLayout>\n );\n}\n\nfunction DefaultLogo({ brandName }: { brandName: string }) {\n return (\n <div className=\"text-center\">\n <h1 className=\"text-xl font-bold\">{brandName}</h1>\n </div>\n );\n}\n\nexport default ResetPasswordPage;\n"],"mappings":"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"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { c as selectContentLocale, d as selectSetContentLocale, f as useAdminStore } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
|
-
import * as React$1 from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { useShallow } from "zustand/shallow";
|
|
5
|
-
|
|
6
|
-
//#region src/client/runtime/locale-scope.tsx
|
|
7
|
-
/**
|
|
8
|
-
* LocaleScope Context
|
|
9
|
-
*
|
|
10
|
-
* Provides scoped locale state for nested forms (e.g., ResourceSheet).
|
|
11
|
-
* When a component is wrapped in LocaleScopeProvider, locale changes
|
|
12
|
-
* affect only that scope, not the global content locale.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* // In ResourceSheet - creates isolated locale scope
|
|
17
|
-
* <LocaleScopeProvider>
|
|
18
|
-
* <FormView ... />
|
|
19
|
-
* </LocaleScopeProvider>
|
|
20
|
-
*
|
|
21
|
-
* // In FieldWrapper - uses scoped locale if available
|
|
22
|
-
* const { locale, setLocale } = useScopedLocale();
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
const LocaleScopeContext = React$1.createContext(null);
|
|
26
|
-
/**
|
|
27
|
-
* Provides an isolated locale scope for nested forms.
|
|
28
|
-
* Locale changes inside this provider don't affect the global state.
|
|
29
|
-
*/
|
|
30
|
-
function LocaleScopeProvider({ children, initialLocale }) {
|
|
31
|
-
const globalLocale = useAdminStore(selectContentLocale);
|
|
32
|
-
const [scopedLocale, setScopedLocale] = React$1.useState(initialLocale ?? globalLocale);
|
|
33
|
-
const hasModifiedRef = React$1.useRef(false);
|
|
34
|
-
React$1.useEffect(() => {
|
|
35
|
-
if (!hasModifiedRef.current) setScopedLocale(globalLocale);
|
|
36
|
-
}, [globalLocale]);
|
|
37
|
-
const setLocale = React$1.useCallback((locale) => {
|
|
38
|
-
hasModifiedRef.current = true;
|
|
39
|
-
setScopedLocale(locale);
|
|
40
|
-
}, []);
|
|
41
|
-
const value = React$1.useMemo(() => ({
|
|
42
|
-
locale: scopedLocale,
|
|
43
|
-
setLocale,
|
|
44
|
-
isScoped: true
|
|
45
|
-
}), [scopedLocale, setLocale]);
|
|
46
|
-
return /* @__PURE__ */ jsx(LocaleScopeContext.Provider, {
|
|
47
|
-
value,
|
|
48
|
-
children
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Returns scoped locale if in a LocaleScopeProvider, otherwise global locale.
|
|
53
|
-
* Use this in components that need locale-aware behavior (FieldWrapper, FormView).
|
|
54
|
-
*/
|
|
55
|
-
function useScopedLocale() {
|
|
56
|
-
const scopedContext = React$1.useContext(LocaleScopeContext);
|
|
57
|
-
const globalLocale = useAdminStore(selectContentLocale);
|
|
58
|
-
const globalSetLocale = useAdminStore(selectSetContentLocale);
|
|
59
|
-
if (scopedContext) return scopedContext;
|
|
60
|
-
return {
|
|
61
|
-
locale: globalLocale,
|
|
62
|
-
setLocale: globalSetLocale,
|
|
63
|
-
isScoped: false
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//#endregion
|
|
68
|
-
export { LocaleScopeProvider as n, useScopedLocale as r, useShallow as t };
|
|
69
|
-
//# sourceMappingURL=runtime-6VZM878K.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-6VZM878K.mjs","names":["React"],"sources":["../src/client/runtime/locale-scope.tsx"],"sourcesContent":["/**\n * LocaleScope Context\n *\n * Provides scoped locale state for nested forms (e.g., ResourceSheet).\n * When a component is wrapped in LocaleScopeProvider, locale changes\n * affect only that scope, not the global content locale.\n *\n * @example\n * ```tsx\n * // In ResourceSheet - creates isolated locale scope\n * <LocaleScopeProvider>\n * <FormView ... />\n * </LocaleScopeProvider>\n *\n * // In FieldWrapper - uses scoped locale if available\n * const { locale, setLocale } = useScopedLocale();\n * ```\n */\n\nimport * as React from \"react\";\nimport {\n\tselectContentLocale,\n\tselectSetContentLocale,\n\tuseAdminStore,\n} from \"./index\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface LocaleScopeContextValue {\n\t/** Current locale in this scope */\n\tlocale: string;\n\t/** Set locale for this scope only */\n\tsetLocale: (locale: string) => void;\n\t/** Whether we're in a scoped context (nested form) */\n\tisScoped: boolean;\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst LocaleScopeContext = React.createContext<LocaleScopeContextValue | null>(\n\tnull,\n);\n\n// ============================================================================\n// Provider\n// ============================================================================\n\ninterface LocaleScopeProviderProps {\n\tchildren: React.ReactNode;\n\t/** Initial locale (defaults to current global content locale) */\n\tinitialLocale?: string;\n}\n\n/**\n * Provides an isolated locale scope for nested forms.\n * Locale changes inside this provider don't affect the global state.\n */\nexport function LocaleScopeProvider({\n\tchildren,\n\tinitialLocale,\n}: LocaleScopeProviderProps) {\n\tconst globalLocale = useAdminStore(selectContentLocale);\n\tconst [scopedLocale, setScopedLocale] = React.useState(\n\t\tinitialLocale ?? globalLocale,\n\t);\n\n\t// Sync with global locale on mount or when global changes (only if not yet modified)\n\tconst hasModifiedRef = React.useRef(false);\n\tReact.useEffect(() => {\n\t\tif (!hasModifiedRef.current) {\n\t\t\tsetScopedLocale(globalLocale);\n\t\t}\n\t}, [globalLocale]);\n\n\tconst setLocale = React.useCallback((locale: string) => {\n\t\thasModifiedRef.current = true;\n\t\tsetScopedLocale(locale);\n\t}, []);\n\n\tconst value = React.useMemo<LocaleScopeContextValue>(\n\t\t() => ({\n\t\t\tlocale: scopedLocale,\n\t\t\tsetLocale,\n\t\t\tisScoped: true,\n\t\t}),\n\t\t[scopedLocale, setLocale],\n\t);\n\n\treturn (\n\t\t<LocaleScopeContext.Provider value={value}>\n\t\t\t{children}\n\t\t</LocaleScopeContext.Provider>\n\t);\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Returns scoped locale if in a LocaleScopeProvider, otherwise global locale.\n * Use this in components that need locale-aware behavior (FieldWrapper, FormView).\n */\nexport function useScopedLocale(): LocaleScopeContextValue {\n\tconst scopedContext = React.useContext(LocaleScopeContext);\n\tconst globalLocale = useAdminStore(selectContentLocale);\n\tconst globalSetLocale = useAdminStore(selectSetContentLocale);\n\n\t// If we're in a scoped context, use that\n\tif (scopedContext) {\n\t\treturn scopedContext;\n\t}\n\n\t// Otherwise, use global locale\n\treturn {\n\t\tlocale: globalLocale,\n\t\tsetLocale: globalSetLocale,\n\t\tisScoped: false,\n\t};\n}\n\n/**\n * Returns true if we're currently in a scoped locale context (nested form).\n */\nexport function useIsLocaleScopeActive(): boolean {\n\tconst scopedContext = React.useContext(LocaleScopeContext);\n\treturn scopedContext !== null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAM,qBAAqBA,QAAM,cAChC,KACA;;;;;AAgBD,SAAgB,oBAAoB,EACnC,UACA,iBAC4B;CAC5B,MAAM,eAAe,cAAc,oBAAoB;CACvD,MAAM,CAAC,cAAc,mBAAmBA,QAAM,SAC7C,iBAAiB,aACjB;CAGD,MAAM,iBAAiBA,QAAM,OAAO,MAAM;AAC1C,SAAM,gBAAgB;AACrB,MAAI,CAAC,eAAe,QACnB,iBAAgB,aAAa;IAE5B,CAAC,aAAa,CAAC;CAElB,MAAM,YAAYA,QAAM,aAAa,WAAmB;AACvD,iBAAe,UAAU;AACzB,kBAAgB,OAAO;IACrB,EAAE,CAAC;CAEN,MAAM,QAAQA,QAAM,eACZ;EACN,QAAQ;EACR;EACA,UAAU;EACV,GACD,CAAC,cAAc,UAAU,CACzB;AAED,QACC,oBAAC,mBAAmB;EAAgB;EAClC;GAC4B;;;;;;AAYhC,SAAgB,kBAA2C;CAC1D,MAAM,gBAAgBA,QAAM,WAAW,mBAAmB;CAC1D,MAAM,eAAe,cAAc,oBAAoB;CACvD,MAAM,kBAAkB,cAAc,uBAAuB;AAG7D,KAAI,cACH,QAAO;AAIR,QAAO;EACN,QAAQ;EACR,WAAW;EACX,UAAU;EACV"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"saved-views.types-BMsz5mCy.d.mts","names":[],"sources":["../src/shared/types/saved-views.types.ts"],"sourcesContent":[],"mappings":";;AAUA;AAsBA;AAUA;AAUA;AAQA;;;;KAlDY,cAAA;;;;KAsBA,WAAA,+BAIT;;;;UAMc,UAAA;;;YAGN;SACH;;;;;UAMS,UAAA;;;;;;;UAQA,iBAAA;WACP;cACG"}
|
package/dist/server.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../src/server/adapters/tanstack.ts","../src/server/adapters/nextjs.ts"],"sourcesContent":[],"mappings":";;;;;;;AAiJA;;AAA4D,UA3H3C,wBAAA,CA2H2C;EAC7B;;;OAxHxB;;;;;EAwHsD,SAAA,CAAA,EAAA,MAAA;;;;ACpH7D;EAoEgB,YAAA,CAAA,EAAA,MAAA;EACd;;;;EAIA,aAAA,CAAA,EAAA,MAAA;;;;;AAQoD,UD7DrC,iBAAA,CC6DqC;EAAO,OAAA,EAAA;IAyFvC,OAAA,EDpJT,OCoJS;IACpB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;EACA,CAAA;EAES,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBD/GK,uBAAA;;;;;GAKb;;GAC6C,sBAAiB;;;;;;;;;;;;;;;;;;;;;iBA8CjD,2BAAA;;;OAA4C;;;GAChB,sBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,UCpH5C,yBAAA,CDoH4C;EAAA;;;OChHtD;EAJU;AAoEjB;;;EAGE,SAAA,CAAA,EAAA,MAAA;EACA;;;;EAS0C,YAAA,CAAA,EAAA,MAAA;EAAkB;;;AAyF9D;EACE,cAAA,CAAA,EAAA,MAAA,EAAA;EACA;;;;EAID,WAAA,CAAA,EAAA,MAAA,EAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBA5Ge,wBAAA;;;;;;;GAYb,sCACyC,YAAU,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyFxC,mBAAA;;;;WAIX;OACJ;IACN,QADc,WAAA"}
|
package/dist/server.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":["getApp","payload: PreviewTokenPayload"],"sources":["../src/server/auth-helpers.ts","../src/server/adapters/tanstack.ts","../src/server/adapters/nextjs.ts","../src/server/modules/admin-preferences/collections/admin-preferences.collection.ts","../src/server/modules/admin-preferences/collections/saved-views.collection.ts","../src/server/modules/admin/functions/locales.ts","../src/server/modules/admin/functions/preview.ts","../src/server/modules/admin/functions/setup.ts","../src/server/modules/admin/index.ts"],"sourcesContent":["/**\n * SSR Auth Helpers\n *\n * Server-side utilities for authentication in SSR frameworks.\n * These helpers work with Better Auth to check session on the server.\n *\n * @example TanStack Router\n * ```ts\n * import { requireAdminAuth } from \"@questpie/admin/server\";\n *\n * export const Route = createFileRoute(\"/admin\")({\n * beforeLoad: async ({ context }) => {\n * const redirect = await requireAdminAuth({\n * request: context.request,\n * cms,\n * loginPath: \"/admin/login\",\n * });\n * if (redirect) throw redirect;\n * },\n * });\n * ```\n */\n\nimport type { Questpie } from \"questpie\";\n\n/**\n * Session object from Better Auth\n */\nexport interface AuthSession {\n user: {\n id: string;\n email: string;\n name?: string | null;\n role?: string | null;\n [key: string]: unknown;\n };\n session: {\n id: string;\n userId: string;\n expiresAt: Date;\n [key: string]: unknown;\n };\n}\n\n/**\n * Options for requireAdminAuth\n */\nexport interface RequireAdminAuthOptions {\n /**\n * The incoming request\n */\n request: Request;\n\n /**\n * The CMS instance with auth configured\n */\n cms: Questpie<any>;\n\n /**\n * Path to redirect to when not authenticated\n * @default \"/admin/login\"\n */\n loginPath?: string;\n\n /**\n * Required role for access\n * @default \"admin\"\n */\n requiredRole?: string;\n\n /**\n * Query parameter name for redirect URL\n * @default \"redirect\"\n */\n redirectParam?: string;\n}\n\n/**\n * Options for getAdminSession\n */\nexport interface GetAdminSessionOptions {\n /**\n * The incoming request\n */\n request: Request;\n\n /**\n * The CMS instance with auth configured\n */\n cms: Questpie<any>;\n}\n\n/**\n * Check if user is authenticated with required role on the server.\n * Returns a redirect Response if not authenticated, null if authenticated.\n *\n * Use this in server loaders/middleware to protect routes.\n *\n * @example TanStack Router\n * ```ts\n * export const Route = createFileRoute(\"/admin\")({\n * beforeLoad: async ({ context }) => {\n * const redirect = await requireAdminAuth({\n * request: context.request,\n * cms,\n * loginPath: \"/admin/login\",\n * });\n * if (redirect) throw redirect;\n * },\n * });\n * ```\n *\n * @example Next.js Middleware\n * ```ts\n * export async function middleware(request: NextRequest) {\n * const redirect = await requireAdminAuth({\n * request,\n * cms,\n * loginPath: \"/admin/login\",\n * });\n * if (redirect) return redirect;\n * return NextResponse.next();\n * }\n * ```\n */\nexport async function requireAdminAuth({\n request,\n cms,\n loginPath = \"/admin/login\",\n requiredRole = \"admin\",\n redirectParam = \"redirect\",\n}: RequireAdminAuthOptions): Promise<Response | null> {\n // Check if auth is configured\n if (!cms.auth) {\n console.warn(\"requireAdminAuth: Auth not configured on CMS instance\");\n return null;\n }\n\n try {\n // Get session from Better Auth\n const session = await cms.auth.api.getSession({\n headers: request.headers,\n });\n\n // No session - redirect to login\n if (!session || !session.user) {\n const currentUrl = new URL(request.url);\n const redirectUrl = new URL(loginPath, currentUrl.origin);\n redirectUrl.searchParams.set(redirectParam, currentUrl.pathname);\n return Response.redirect(redirectUrl.toString(), 302);\n }\n\n // Check role - cast to any because role is added by Better Auth admin plugin\n const userRole = (session.user as any).role;\n if (userRole !== requiredRole) {\n const currentUrl = new URL(request.url);\n const redirectUrl = new URL(loginPath, currentUrl.origin);\n redirectUrl.searchParams.set(redirectParam, currentUrl.pathname);\n return Response.redirect(redirectUrl.toString(), 302);\n }\n\n // Authenticated with correct role\n return null;\n } catch (error) {\n console.error(\"requireAdminAuth: Error checking session\", error);\n // On error, redirect to login for safety\n const currentUrl = new URL(request.url);\n const redirectUrl = new URL(loginPath, currentUrl.origin);\n return Response.redirect(redirectUrl.toString(), 302);\n }\n}\n\n/**\n * Get the current admin session on the server.\n * Returns null if not authenticated.\n *\n * Use this when you need access to the session data in server code.\n *\n * @example\n * ```ts\n * const session = await getAdminSession({ request, cms });\n * if (!session) {\n * return redirect(\"/admin/login\");\n * }\n * console.log(\"User:\", session.user.name);\n * ```\n */\nexport async function getAdminSession({\n request,\n cms,\n}: GetAdminSessionOptions): Promise<AuthSession | null> {\n // Check if auth is configured\n if (!cms.auth) {\n return null;\n }\n\n try {\n const session = await cms.auth.api.getSession({\n headers: request.headers,\n });\n\n if (!session || !session.user) {\n return null;\n }\n\n return session as AuthSession;\n } catch (error) {\n console.error(\"getAdminSession: Error getting session\", error);\n return null;\n }\n}\n\n/**\n * Check if the current user has admin role on the server.\n *\n * @example\n * ```ts\n * const isAdmin = await isAdminUser({ request, cms });\n * if (!isAdmin) {\n * return json({ error: \"Unauthorized\" }, { status: 403 });\n * }\n * ```\n */\nexport async function isAdminUser({\n request,\n cms,\n requiredRole = \"admin\",\n}: GetAdminSessionOptions & { requiredRole?: string }): Promise<boolean> {\n const session = await getAdminSession({ request, cms });\n // Cast to any to access role - it's added by Better Auth admin plugin\n return (session?.user as any)?.role === requiredRole;\n}\n","/**\n * TanStack Router Adapter\n *\n * Convenience helpers for TanStack Router/Start integration.\n *\n * @example\n * ```ts\n * import { createTanStackAuthGuard } from \"@questpie/admin/server/adapters/tanstack\";\n * import { cms } from \"~/questpie/server/cms\";\n *\n * export const Route = createFileRoute(\"/admin\")({\n * beforeLoad: createTanStackAuthGuard({ cms, loginPath: \"/admin/login\" }),\n * });\n * ```\n */\n\nimport type { Questpie } from \"questpie\";\nimport { requireAdminAuth } from \"../auth-helpers.js\";\n\n/**\n * Options for TanStack auth guard\n */\nexport interface TanStackAuthGuardOptions {\n /**\n * The CMS instance with auth configured\n */\n cms: Questpie<any>;\n\n /**\n * Path to redirect to when not authenticated\n * @default \"/admin/login\"\n */\n loginPath?: string;\n\n /**\n * Required role for access\n * @default \"admin\"\n */\n requiredRole?: string;\n\n /**\n * Query parameter name for redirect URL\n * @default \"redirect\"\n */\n redirectParam?: string;\n}\n\n/**\n * Context passed to TanStack Router beforeLoad\n */\nexport interface BeforeLoadContext {\n context: {\n request: Request;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/**\n * Create a TanStack Router beforeLoad guard for admin authentication.\n *\n * Returns a function that can be used as beforeLoad in route definitions.\n * Throws a redirect Response when authentication fails.\n *\n * @example\n * ```ts\n * import { createFileRoute } from \"@tanstack/react-router\";\n * import { createTanStackAuthGuard } from \"@questpie/admin/server/adapters/tanstack\";\n * import { cms } from \"~/questpie/server/cms\";\n *\n * export const Route = createFileRoute(\"/admin\")({\n * beforeLoad: createTanStackAuthGuard({\n * cms,\n * loginPath: \"/admin/login\",\n * requiredRole: \"admin\",\n * }),\n * component: AdminLayout,\n * });\n * ```\n *\n * @example With custom context\n * ```ts\n * export const Route = createFileRoute(\"/admin\")({\n * beforeLoad: async (ctx) => {\n * // Run auth guard\n * await createTanStackAuthGuard({ cms })(ctx);\n *\n * // Add additional context\n * return { user: ctx.context.user };\n * },\n * });\n * ```\n */\nexport function createTanStackAuthGuard({\n cms,\n loginPath = \"/admin/login\",\n requiredRole = \"admin\",\n redirectParam = \"redirect\",\n}: TanStackAuthGuardOptions) {\n return async function beforeLoad({ context }: BeforeLoadContext) {\n const request = context.request;\n\n if (!request) {\n console.warn(\n \"createTanStackAuthGuard: No request in context. \" +\n \"Make sure you're using TanStack Start with SSR enabled.\",\n );\n return;\n }\n\n const redirect = await requireAdminAuth({\n request,\n cms,\n loginPath,\n requiredRole,\n redirectParam,\n });\n\n if (redirect) {\n // TanStack Router expects thrown Response for redirects\n throw redirect;\n }\n };\n}\n\n/**\n * Create a TanStack Router loader that injects the admin session into context.\n *\n * Use this when you need access to the session in your components.\n *\n * @example\n * ```ts\n * import { createTanStackSessionLoader } from \"@questpie/admin/server/adapters/tanstack\";\n *\n * export const Route = createFileRoute(\"/admin\")({\n * loader: createTanStackSessionLoader({ cms }),\n * component: AdminLayout,\n * });\n *\n * function AdminLayout() {\n * const { session } = Route.useLoaderData();\n * return <div>Hello {session?.user?.name}</div>;\n * }\n * ```\n */\nexport function createTanStackSessionLoader({ cms }: { cms: Questpie<any> }) {\n return async function loader({ context }: BeforeLoadContext) {\n const request = context.request;\n\n if (!request || !cms.auth) {\n return { session: null };\n }\n\n try {\n const session = await cms.auth.api.getSession({\n headers: request.headers,\n });\n\n return { session: session ?? null };\n } catch {\n return { session: null };\n }\n };\n}\n","/**\n * Next.js Adapter\n *\n * Convenience helpers for Next.js middleware integration.\n *\n * @example App Router middleware\n * ```ts\n * // middleware.ts\n * import { createNextAuthMiddleware } from \"@questpie/admin/server/adapters/nextjs\";\n * import { cms } from \"./questpie/server/cms\";\n *\n * export default createNextAuthMiddleware({\n * cms,\n * loginPath: \"/admin/login\",\n * protectedPaths: [\"/admin\"],\n * publicPaths: [\"/admin/login\", \"/admin/forgot-password\"],\n * });\n *\n * export const config = {\n * matcher: [\"/admin/:path*\"],\n * };\n * ```\n */\n\nimport type { Questpie } from \"questpie\";\nimport { getAdminSession, requireAdminAuth } from \"../auth-helpers.js\";\n\n/**\n * Options for Next.js auth middleware\n */\nexport interface NextAuthMiddlewareOptions {\n /**\n * The CMS instance with auth configured\n */\n cms: Questpie<any>;\n\n /**\n * Path to redirect to when not authenticated\n * @default \"/admin/login\"\n */\n loginPath?: string;\n\n /**\n * Required role for access\n * @default \"admin\"\n */\n requiredRole?: string;\n\n /**\n * Paths that require authentication (uses startsWith matching)\n * @default [\"/admin\"]\n */\n protectedPaths?: string[];\n\n /**\n * Paths within protectedPaths that should be publicly accessible\n * @default [\"/admin/login\", \"/admin/forgot-password\", \"/admin/reset-password\", \"/admin/accept-invite\"]\n */\n publicPaths?: string[];\n\n /**\n * Query parameter name for redirect URL\n * @default \"redirect\"\n */\n redirectParam?: string;\n}\n\n/**\n * Check if a path matches any of the given patterns\n */\nfunction pathMatches(pathname: string, patterns: string[]): boolean {\n return patterns.some(\n (pattern) =>\n pathname === pattern ||\n pathname.startsWith(`${pattern}/`) ||\n pathname.startsWith(pattern),\n );\n}\n\n/**\n * Create a Next.js middleware for admin authentication.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { createNextAuthMiddleware } from \"@questpie/admin/server/adapters/nextjs\";\n * import { cms } from \"./questpie/server/cms\";\n *\n * export default createNextAuthMiddleware({\n * cms,\n * loginPath: \"/admin/login\",\n * });\n *\n * export const config = {\n * matcher: [\"/admin/:path*\"],\n * };\n * ```\n */\nexport function createNextAuthMiddleware({\n cms,\n loginPath = \"/admin/login\",\n requiredRole = \"admin\",\n protectedPaths = [\"/admin\"],\n publicPaths = [\n \"/admin/login\",\n \"/admin/forgot-password\",\n \"/admin/reset-password\",\n \"/admin/accept-invite\",\n ],\n redirectParam = \"redirect\",\n}: NextAuthMiddlewareOptions) {\n return async function middleware(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const pathname = url.pathname;\n\n // Check if this is a protected path\n const isProtected = pathMatches(pathname, protectedPaths);\n const isPublic = pathMatches(pathname, publicPaths);\n\n // If not protected or is public, continue\n if (!isProtected || isPublic) {\n // Return a \"continue\" response\n // In Next.js middleware, returning undefined or NextResponse.next() continues\n // Since we're using standard Response, we'll return a special header\n return new Response(null, {\n status: 200,\n headers: {\n \"x-middleware-next\": \"1\",\n },\n });\n }\n\n // Check authentication\n const redirect = await requireAdminAuth({\n request,\n cms,\n loginPath,\n requiredRole,\n redirectParam,\n });\n\n if (redirect) {\n return redirect;\n }\n\n // Authenticated - continue\n return new Response(null, {\n status: 200,\n headers: {\n \"x-middleware-next\": \"1\",\n },\n });\n };\n}\n\n/**\n * Get the admin session in a Next.js server component or API route.\n *\n * @example Server Component\n * ```tsx\n * // app/admin/layout.tsx\n * import { getNextAdminSession } from \"@questpie/admin/server/adapters/nextjs\";\n * import { headers } from \"next/headers\";\n * import { cms } from \"~/questpie/server/cms\";\n *\n * export default async function AdminLayout({ children }) {\n * const headersList = headers();\n * const session = await getNextAdminSession({\n * headers: headersList,\n * cms,\n * });\n *\n * if (!session) {\n * redirect(\"/admin/login\");\n * }\n *\n * return <div>{children}</div>;\n * }\n * ```\n *\n * @example API Route\n * ```ts\n * // app/api/admin/users/route.ts\n * import { getNextAdminSession } from \"@questpie/admin/server/adapters/nextjs\";\n * import { cms } from \"~/questpie/server/cms\";\n *\n * export async function GET(request: Request) {\n * const session = await getNextAdminSession({\n * headers: request.headers,\n * cms,\n * });\n *\n * if (!session || session.user.role !== \"admin\") {\n * return Response.json({ error: \"Unauthorized\" }, { status: 401 });\n * }\n *\n * // ... handle request\n * }\n * ```\n */\nexport async function getNextAdminSession({\n headers,\n cms,\n}: {\n headers: Headers;\n cms: Questpie<any>;\n}) {\n // Create a minimal request object for getAdminSession\n const request = new Request(\"http://localhost\", { headers });\n return getAdminSession({ request, cms });\n}\n","import { jsonb, uniqueIndex, varchar } from \"drizzle-orm/pg-core\";\nimport { q } from \"questpie\";\n\n/**\n * Admin Preferences Collection\n *\n * Stores user-specific preferences for admin UI state.\n * This includes view configurations (columns, filters, sort)\n * that persist across devices and sessions.\n *\n * Key format: \"viewState:{collectionName}\" for view state preferences\n *\n * @example\n * ```ts\n * import { adminModule } from \"@questpie/admin/server\";\n *\n * const cms = q({ name: \"my-app\" })\n * .use(adminModule)\n * .collections({ ... })\n * .build({ ... });\n *\n * // Access preferences\n * const prefs = await cms.api.collections.admin_preferences.findOne({\n * where: { userId: currentUser.id, key: \"viewState:posts\" }\n * });\n * ```\n */\nexport const adminPreferencesCollection = q\n\t.collection(\"admin_preferences\")\n\t.fields({\n\t\t// User who owns this preference\n\t\tuserId: varchar(\"user_id\", { length: 255 }).notNull(),\n\n\t\t// Preference key (e.g., \"viewState:posts\")\n\t\tkey: varchar(\"key\", { length: 255 }).notNull(),\n\n\t\t// Preference value (JSON)\n\t\tvalue: jsonb(\"value\").notNull(),\n\t})\n\t.options({\n\t\ttimestamps: true,\n\t})\n\t.indexes(({ table }) => [\n\t\t// Unique constraint on userId + key\n\t\t// Type assertion needed due to Drizzle ORM duplicate dependency resolution\n\t\tuniqueIndex(\"admin_preferences_user_key_idx\").on(\n\t\t\ttable.userId as any,\n\t\t\ttable.key as any,\n\t\t),\n\t]);\n","import { q } from \"questpie\";\nimport { boolean, jsonb, varchar } from \"drizzle-orm/pg-core\";\nimport type { ViewConfiguration } from \"../../../../shared/types/saved-views.types.js\";\n\n// Re-export types from shared\nexport type {\n FilterOperator,\n FilterRule,\n SortConfig,\n ViewConfiguration,\n} from \"../../../../shared/types/saved-views.types.js\";\n\n/**\n * Admin Saved Views Collection\n *\n * Stores user-specific view configurations for collection lists.\n * Each view can contain:\n * - Filter rules (field/operator/value combinations)\n * - Sort configuration (field/direction)\n * - Visible columns selection\n *\n * @example\n * ```ts\n * import { adminModule } from \"@questpie/admin/server\";\n *\n * const cms = q({ name: \"my-app\" })\n * .use(starterModule)\n * .use(adminModule)\n * .collections({ ... })\n * .build({ ... });\n *\n * // Access saved views\n * const views = await cms.api.collections.adminSavedViews.find({\n * where: { collectionName: \"posts\", userId: currentUser.id }\n * });\n * ```\n */\nexport const savedViewsCollection = q\n .collection(\"admin_saved_views\")\n .fields({\n // User who owns this saved view\n userId: varchar(\"user_id\", { length: 255 }).notNull(),\n\n // Target collection for this view\n collectionName: varchar(\"collection_name\", { length: 255 }).notNull(),\n\n // Display name for the view\n name: varchar(\"name\", { length: 255 }).notNull(),\n\n // View configuration (filters, sort, columns)\n configuration: jsonb(\"configuration\").notNull().$type<ViewConfiguration>(),\n\n // Whether this is the default view for the user/collection\n isDefault: boolean(\"is_default\").default(false).notNull(),\n })\n .options({\n timestamps: true,\n });\n","/**\n * Content Locales Functions\n *\n * Functions for retrieving available content locales from the CMS configuration.\n * Used by the admin panel to populate locale switchers and validate locale selection.\n */\n\nimport { fn, type Questpie } from \"questpie\";\nimport { z } from \"zod\";\n\n// ============================================================================\n// Type Helpers\n// ============================================================================\n\n/**\n * Helper to get typed CMS app from handler context.\n */\nfunction getApp(ctx: { app: unknown }): Questpie<any> {\n\treturn ctx.app as Questpie<any>;\n}\n\n// ============================================================================\n// Schema Definitions\n// ============================================================================\n\nconst getContentLocalesSchema = z.object({}).optional();\n\nconst getContentLocalesOutputSchema = z.object({\n\tlocales: z.array(\n\t\tz.object({\n\t\t\tcode: z.string(),\n\t\t\tlabel: z.string().optional(),\n\t\t\tfallback: z.boolean().optional(),\n\t\t\tflagCountryCode: z.string().optional(),\n\t\t}),\n\t),\n\tdefaultLocale: z.string(),\n\tfallbacks: z.record(z.string(), z.string()).optional(),\n});\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get available content locales from CMS configuration.\n *\n * Returns the list of available locales for content localization,\n * the default locale, and any fallback mappings.\n *\n * @example\n * ```ts\n * const result = await client.rpc.getContentLocales({});\n * // {\n * // locales: [\n * // { code: \"en\", label: \"English\", fallback: true },\n * // { code: \"sk\", label: \"Slovenčina\" },\n * // ],\n * // defaultLocale: \"en\",\n * // fallbacks: { \"en-GB\": \"en\" },\n * // }\n * ```\n */\nexport const getContentLocales = fn({\n\ttype: \"query\",\n\tschema: getContentLocalesSchema,\n\toutputSchema: getContentLocalesOutputSchema,\n\thandler: async (ctx) => {\n\t\tconst cms = getApp(ctx);\n\t\tconst localeConfig = cms.config.locale;\n\n\t\t// If no locale config, return sensible defaults\n\t\tif (!localeConfig) {\n\t\t\treturn {\n\t\t\t\tlocales: [{ code: \"en\", label: \"English\", fallback: true }],\n\t\t\t\tdefaultLocale: \"en\",\n\t\t\t};\n\t\t}\n\n\t\t// Resolve locales (can be async function)\n\t\tconst locales =\n\t\t\ttypeof localeConfig.locales === \"function\"\n\t\t\t\t? await localeConfig.locales()\n\t\t\t\t: localeConfig.locales;\n\n\t\treturn {\n\t\t\tlocales: locales.map(\n\t\t\t\t(l: {\n\t\t\t\t\tcode: string;\n\t\t\t\t\tlabel?: string;\n\t\t\t\t\tfallback?: boolean;\n\t\t\t\t\tflagCountryCode?: string;\n\t\t\t\t}) => ({\n\t\t\t\t\tcode: l.code,\n\t\t\t\t\tlabel: l.label,\n\t\t\t\t\tfallback: l.fallback,\n\t\t\t\t\tflagCountryCode: l.flagCountryCode,\n\t\t\t\t}),\n\t\t\t),\n\t\t\tdefaultLocale: localeConfig.defaultLocale,\n\t\t\tfallbacks: localeConfig.fallbacks,\n\t\t};\n\t},\n});\n\n// ============================================================================\n// Export Bundle\n// ============================================================================\n\n/**\n * Bundle of locale-related functions.\n */\nexport const localeFunctions = {\n\tgetContentLocales,\n} as const;\n","/**\n * Preview Functions - Server-side\n *\n * RPC functions for draft mode preview.\n * Handles token minting for secure, shareable preview links.\n *\n * Browser-safe utilities (isDraftMode, createDraftModeCookie, etc.) are in @questpie/admin/shared\n */\n\nimport { getPreviewSecret } from \"#questpie/admin/shared/preview-utils.js\";\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { fn } from \"questpie\";\nimport { z } from \"zod\";\n\n// ============================================================================\n// Token Utilities\n// ============================================================================\n\nfunction base64UrlEncode(input: string | Buffer): string {\n\tconst buffer = Buffer.isBuffer(input) ? input : Buffer.from(input);\n\treturn buffer\n\t\t.toString(\"base64\")\n\t\t.replace(/\\+/g, \"-\")\n\t\t.replace(/\\//g, \"_\")\n\t\t.replace(/=+$/g, \"\");\n}\n\nfunction base64UrlDecode(input: string): string {\n\tlet base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\tconst padding = base64.length % 4;\n\tif (padding) {\n\t\tbase64 += \"=\".repeat(4 - padding);\n\t}\n\treturn Buffer.from(base64, \"base64\").toString(\"utf8\");\n}\n\n// ============================================================================\n// Schema Definitions\n// ============================================================================\n\nconst mintPreviewTokenSchema = z.object({\n\tpath: z.string().min(1, \"Path is required\"),\n\tttlMs: z.number().positive().optional(),\n});\n\nconst mintPreviewTokenOutputSchema = z.object({\n\ttoken: z.string(),\n\texpiresAt: z.number(),\n});\n\nconst verifyPreviewTokenSchema = z.object({\n\ttoken: z.string(),\n});\n\nconst verifyPreviewTokenOutputSchema = z.object({\n\tvalid: z.boolean(),\n\tpath: z.string().optional(),\n\terror: z.string().optional(),\n});\n\n// ============================================================================\n// Preview Token Payload\n// ============================================================================\n\nexport interface PreviewTokenPayload {\n\tpath: string;\n\texp: number;\n}\n\n// ============================================================================\n// Functions Factory\n// ============================================================================\n\nconst DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour\n\n/**\n * Create preview-related RPC functions.\n *\n * @param secret - Secret key for signing tokens\n * @returns Object with preview functions\n */\nexport function createPreviewFunctions(secret: string) {\n\tconst signPayload = (payload: string): string => {\n\t\tconst signature = createHmac(\"sha256\", secret).update(payload).digest();\n\t\treturn base64UrlEncode(signature);\n\t};\n\n\t/**\n\t * Mint a preview token for draft mode access.\n\t *\n\t * Requires authenticated admin session.\n\t * Token contains the target path and expiration time.\n\t *\n\t * @example\n\t * ```ts\n\t * const { token } = await client.rpc.mintPreviewToken({\n\t * path: \"/pages/about\",\n\t * ttlMs: 30 * 60 * 1000, // 30 minutes\n\t * });\n\t * // Use: /api/preview?token=${token}\n\t * ```\n\t */\n\tconst mintPreviewToken = fn({\n\t\ttype: \"mutation\",\n\t\tschema: mintPreviewTokenSchema,\n\t\toutputSchema: mintPreviewTokenOutputSchema,\n\t\thandler: async ({ input, session }) => {\n\t\t\t// Require authenticated admin session\n\t\t\tif (!session) {\n\t\t\t\tthrow new Error(\"Unauthorized: Admin session required\");\n\t\t\t}\n\n\t\t\tconst { path, ttlMs = DEFAULT_TTL_MS } = input;\n\t\t\tconst expiresAt = Date.now() + ttlMs;\n\n\t\t\tconst payload: PreviewTokenPayload = { path, exp: expiresAt };\n\t\t\tconst payloadString = JSON.stringify(payload);\n\t\t\tconst encodedPayload = base64UrlEncode(payloadString);\n\t\t\tconst signature = signPayload(encodedPayload);\n\n\t\t\treturn {\n\t\t\t\ttoken: `${encodedPayload}.${signature}`,\n\t\t\t\texpiresAt,\n\t\t\t};\n\t\t},\n\t});\n\n\t/**\n\t * Verify a preview token.\n\t *\n\t * Used by the preview route to validate tokens before setting draft mode cookie.\n\t * Does not require authentication (token IS the authentication).\n\t *\n\t * @example\n\t * ```ts\n\t * const result = await client.rpc.verifyPreviewToken({ token });\n\t * if (result.valid) {\n\t * // Redirect to result.path with draft mode cookie\n\t * }\n\t * ```\n\t */\n\tconst verifyPreviewToken = fn({\n\t\ttype: \"query\",\n\t\tschema: verifyPreviewTokenSchema,\n\t\toutputSchema: verifyPreviewTokenOutputSchema,\n\t\thandler: async ({ input }) => {\n\t\t\tconst { token } = input;\n\n\t\t\tconst [encodedPayload, signature] = token.split(\".\");\n\t\t\tif (!encodedPayload || !signature) {\n\t\t\t\treturn { valid: false, error: \"Invalid token format\" };\n\t\t\t}\n\n\t\t\t// Verify signature\n\t\t\tconst expectedSignature = signPayload(encodedPayload);\n\t\t\tconst signatureBuffer = Uint8Array.from(Buffer.from(signature));\n\t\t\tconst expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));\n\n\t\t\tif (signatureBuffer.length !== expectedBuffer.length) {\n\t\t\t\treturn { valid: false, error: \"Invalid signature\" };\n\t\t\t}\n\n\t\t\tif (!timingSafeEqual(signatureBuffer, expectedBuffer)) {\n\t\t\t\treturn { valid: false, error: \"Invalid signature\" };\n\t\t\t}\n\n\t\t\t// Parse and validate payload\n\t\t\ttry {\n\t\t\t\tconst payload = JSON.parse(\n\t\t\t\t\tbase64UrlDecode(encodedPayload),\n\t\t\t\t) as PreviewTokenPayload;\n\n\t\t\t\tif (!payload?.exp || typeof payload.exp !== \"number\") {\n\t\t\t\t\treturn { valid: false, error: \"Invalid payload\" };\n\t\t\t\t}\n\n\t\t\t\tif (payload.exp < Date.now()) {\n\t\t\t\t\treturn { valid: false, error: \"Token expired\" };\n\t\t\t\t}\n\n\t\t\t\tif (!payload.path || typeof payload.path !== \"string\") {\n\t\t\t\t\treturn { valid: false, error: \"Invalid path\" };\n\t\t\t\t}\n\n\t\t\t\treturn { valid: true, path: payload.path };\n\t\t\t} catch {\n\t\t\t\treturn { valid: false, error: \"Invalid payload\" };\n\t\t\t}\n\t\t},\n\t});\n\n\treturn {\n\t\tmintPreviewToken,\n\t\tverifyPreviewToken,\n\t};\n}\n\n// ============================================================================\n// Standalone Token Verification (for route handlers)\n// ============================================================================\n\n/**\n * Verify a preview token without RPC.\n * Used directly in route handlers where RPC is not available.\n *\n * @param token - The preview token to verify\n * @param secret - The secret used to sign the token\n * @returns The payload if valid, null otherwise\n */\nexport function verifyPreviewTokenDirect(\n\ttoken: string,\n\tsecret: string,\n): PreviewTokenPayload | null {\n\tconst [encodedPayload, signature] = token.split(\".\");\n\tif (!encodedPayload || !signature) return null;\n\n\tconst expectedSignature = base64UrlEncode(\n\t\tcreateHmac(\"sha256\", secret).update(encodedPayload).digest(),\n\t);\n\n\tconst signatureBuffer = Uint8Array.from(Buffer.from(signature));\n\tconst expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));\n\n\tif (signatureBuffer.length !== expectedBuffer.length) return null;\n\tif (!timingSafeEqual(signatureBuffer, expectedBuffer)) return null;\n\n\ttry {\n\t\tconst payload = JSON.parse(\n\t\t\tbase64UrlDecode(encodedPayload),\n\t\t) as PreviewTokenPayload;\n\n\t\tif (!payload?.exp || typeof payload.exp !== \"number\") return null;\n\t\tif (payload.exp < Date.now()) return null;\n\t\tif (!payload.path || typeof payload.path !== \"string\") return null;\n\n\t\treturn payload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// ============================================================================\n// Factory & Helpers\n// ============================================================================\n\n/**\n * Create a preview token verifier with bound secret.\n * Use this in route handlers to avoid passing secret repeatedly.\n *\n * @param secret - The secret used to sign tokens (optional, uses env if not provided)\n * @returns A verify function that only needs the token\n *\n * @example\n * ```ts\n * // Create once at module level\n * const verifyPreviewToken = createPreviewTokenVerifier();\n *\n * // Use in route handler\n * const payload = verifyPreviewToken(token);\n * if (!payload) {\n * return new Response(\"Invalid token\", { status: 401 });\n * }\n * ```\n */\nexport function createPreviewTokenVerifier(secret?: string) {\n\tconst resolvedSecret = secret ?? getPreviewSecret();\n\n\treturn (token: string): PreviewTokenPayload | null => {\n\t\treturn verifyPreviewTokenDirect(token, resolvedSecret);\n\t};\n}\n\n// ============================================================================\n// Default Functions Bundle\n// ============================================================================\n\n/**\n * Default preview functions bundle with env-based secret.\n * Used by adminModule to register preview RPC functions.\n */\nexport const previewFunctions = createPreviewFunctions(getPreviewSecret());\n","/**\n * Setup Functions\n *\n * Built-in functions for bootstrapping the first admin user.\n * Solves the chicken-and-egg problem where invitation-based systems\n * need an existing admin to create the first invitation.\n */\n\nimport { eq, sql } from \"drizzle-orm\";\nimport { fn, type Questpie } from \"questpie\";\nimport { z } from \"zod\";\n\n// ============================================================================\n// Type Helpers\n// ============================================================================\n\n/**\n * Helper to get typed CMS app from handler context.\n * Used internally for better IDE support without affecting the public API.\n */\nfunction getApp(ctx: { app: unknown }): Questpie<any> {\n\treturn ctx.app as Questpie<any>;\n}\n\n// ============================================================================\n// Schema Definitions\n// ============================================================================\n\nconst isSetupRequiredSchema = z.object({});\n\nconst isSetupRequiredOutputSchema = z.object({\n\trequired: z.boolean(),\n});\n\nconst createFirstAdminSchema = z.object({\n\temail: z.string().email(\"Invalid email address\"),\n\tpassword: z.string().min(8, \"Password must be at least 8 characters\"),\n\tname: z.string().min(2, \"Name must be at least 2 characters\"),\n});\n\nconst createFirstAdminOutputSchema = z.object({\n\tsuccess: z.boolean(),\n\tuser: z\n\t\t.object({\n\t\t\tid: z.string(),\n\t\t\temail: z.string(),\n\t\t\tname: z.string(),\n\t\t})\n\t\t.optional(),\n\terror: z.string().optional(),\n});\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Check if setup is required (no users exist in the system).\n *\n * @example\n * ```ts\n * const result = await client.rpc.isSetupRequired({});\n * if (result.required) {\n * // Redirect to setup page\n * }\n * ```\n */\nexport const isSetupRequired = fn({\n\ttype: \"query\",\n\tschema: isSetupRequiredSchema,\n\toutputSchema: isSetupRequiredOutputSchema,\n\thandler: async (ctx) => {\n\t\tconst app = getApp(ctx);\n\t\tconst userCollection = app.getCollectionConfig(\"user\");\n\t\tconst result = await app.db\n\t\t\t.select({ count: sql<number>`count(*)::int` })\n\t\t\t.from(userCollection.table);\n\t\treturn { required: result[0].count === 0 };\n\t},\n});\n\n/**\n * Create the first admin user in the system.\n * This function only works when no users exist (setup mode).\n *\n * Security: Once any user exists, this function will refuse to create more users.\n * This prevents unauthorized admin creation after initial setup.\n *\n * @example\n * ```ts\n * const result = await client.rpc.createFirstAdmin({\n * email: \"admin@example.com\",\n * password: \"securepassword123\",\n * name: \"Admin User\",\n * });\n *\n * if (result.success) {\n * // Redirect to login page\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport const createFirstAdmin = fn({\n\ttype: \"mutation\",\n\tschema: createFirstAdminSchema,\n\toutputSchema: createFirstAdminOutputSchema,\n\thandler: async (ctx) => {\n\t\tconst app = getApp(ctx);\n\t\tconst input = ctx.input as z.infer<typeof createFirstAdminSchema>;\n\t\tconst userCollection = app.getCollectionConfig(\"user\");\n\n\t\t// Check if setup already completed (any users exist)\n\t\tconst checkResult = await app.db\n\t\t\t.select({ count: sql<number>`count(*)::int` })\n\t\t\t.from(userCollection.table);\n\n\t\tif (checkResult[0].count > 0) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Setup already completed - users exist in the system\",\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\t// Create user via Better Auth signUp\n\t\t\tconst signUpResult = await app.auth.api.signUpEmail({\n\t\t\t\tbody: {\n\t\t\t\t\temail: input.email,\n\t\t\t\t\tpassword: input.password,\n\t\t\t\t\tname: input.name,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (!signUpResult.user) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Failed to create user account\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Update role to admin and verify email (first user is always admin)\n\t\t\t// TODO: we can also use our builder pattern for admin functions for proper typesafety here !\n\t\t\tawait app.db\n\t\t\t\t.update(userCollection.table)\n\t\t\t\t.set({\n\t\t\t\t\trole: \"admin\",\n\t\t\t\t\temailVerified: true,\n\t\t\t\t} as any)\n\t\t\t\t.where(eq(userCollection.table.id, signUpResult.user.id));\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tuser: {\n\t\t\t\t\tid: signUpResult.user.id,\n\t\t\t\t\temail: signUpResult.user.email,\n\t\t\t\t\tname: signUpResult.user.name,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: \"An unexpected error occurred\",\n\t\t\t};\n\t\t}\n\t},\n});\n\n// ============================================================================\n// Export Bundle\n// ============================================================================\n\n/**\n * Bundle of setup-related functions.\n */\nexport const setupFunctions = {\n\tisSetupRequired,\n\tcreateFirstAdmin,\n} as const;\n","/**\n * Admin Module\n *\n * Complete backend module for running the QuestPie admin panel.\n * This is the main entry point for setting up the admin backend.\n *\n * Includes:\n * - Auth collections (users, sessions, accounts, verifications, apikeys)\n * - Assets collection with file upload support\n * - Admin saved views collection (named view configurations)\n * - Admin preferences collection (user-specific view state)\n * - Setup functions for bootstrapping first admin\n * - Core auth options (Better Auth configuration)\n *\n * @example\n * ```ts\n * import { q } from \"questpie\";\n * import { adminModule } from \"@questpie/admin/server\";\n *\n * const cms = q({ name: \"my-app\" })\n * .use(adminModule)\n * .collections({\n * posts: postsCollection,\n * })\n * .build({\n * db: { url: process.env.DATABASE_URL },\n * storage: { driver: s3Driver(...) },\n * });\n * ```\n */\n\nimport { q, starterModule } from \"questpie\";\nimport { adminPreferencesCollection } from \"../admin-preferences/collections/admin-preferences.collection.js\";\nimport { savedViewsCollection } from \"../admin-preferences/collections/saved-views.collection.js\";\nimport { localeFunctions } from \"./functions/locales.js\";\nimport { previewFunctions } from \"./functions/preview.js\";\nimport { setupFunctions } from \"./functions/setup.js\";\n\n// Re-export admin preferences collection\nexport { adminPreferencesCollection } from \"../admin-preferences/collections/admin-preferences.collection.js\";\n// Re-export saved views types\nexport {\n\ttype FilterOperator,\n\ttype FilterRule,\n\ttype SortConfig,\n\tsavedViewsCollection,\n\ttype ViewConfiguration,\n} from \"../admin-preferences/collections/saved-views.collection.js\";\n// Re-export locale functions for individual use\nexport { getContentLocales, localeFunctions } from \"./functions/locales.js\";\n// Re-export server-only preview functions (crypto-based token verification)\n// For browser-safe utilities (isDraftMode, createDraftModeCookie, etc.), use @questpie/admin/shared\nexport {\n\tcreatePreviewFunctions,\n\tcreatePreviewTokenVerifier,\n\ttype PreviewTokenPayload,\n\tpreviewFunctions,\n\tverifyPreviewTokenDirect,\n} from \"./functions/preview.js\";\n// Re-export setup functions for individual use\nexport {\n\tcreateFirstAdmin,\n\tisSetupRequired,\n\tsetupFunctions,\n} from \"./functions/setup.js\";\n\n/**\n * Admin Module - the complete backend for QuestPie admin panel.\n *\n * This module provides everything needed to run the admin panel:\n * - User authentication (Better Auth) - from starterModule\n * - File uploads (assets) - from starterModule\n * - Saved views for collection filters (named configurations)\n * - User preferences (view state synced across devices)\n * - Setup flow for first admin creation\n *\n * @example\n * ```ts\n * import { q } from \"questpie\";\n * import { adminModule } from \"@questpie/admin/server\";\n *\n * const cms = q({ name: \"my-app\" })\n * .use(adminModule)\n * .collections({\n * posts: postsCollection,\n * })\n * .build({\n * db: { url: process.env.DATABASE_URL },\n * });\n * ```\n *\n * @example\n * ```ts\n * // Extend assets collection with custom fields\n * import { q, collection, varchar } from \"questpie\";\n * import { adminModule } from \"@questpie/admin/server\";\n *\n * const cms = q({ name: \"my-app\" })\n * .use(adminModule)\n * .collections({\n * // Override assets with additional fields\n * assets: adminModule.state.collections.assets.merge(\n * collection(\"assets\").fields({\n * folder: varchar(\"folder\", { length: 255 }),\n * tags: varchar(\"tags\", { length: 1000 }),\n * })\n * ),\n * })\n * .build({ ... });\n * ```\n */\nexport const adminModule = q({ name: \"questpie-admin\" })\n\t// Include all starterModule functionality (auth, assets)\n\t.use(starterModule)\n\t// Add admin-specific collections\n\t.collections({\n\t\tadmin_saved_views: savedViewsCollection,\n\t\tadmin_preferences: adminPreferencesCollection,\n\t})\n\t// Add setup, locale, and preview functions\n\t.functions({\n\t\t...setupFunctions,\n\t\t...localeFunctions,\n\t\t...previewFunctions,\n\t});\n"],"mappings":"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"}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { a as selectBasePath, f as useAdminStore, g as cn, h as Button, l as selectNavigate, o as selectBrandName, s as selectClient } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
|
-
import { a as FieldContent, c as FieldGroup, f as Input, i as Field, l as FieldLabel, n as Alert, r as AlertDescription, s as FieldError, t as AuthLayout } from "./auth-layout-M8K8_q5R.mjs";
|
|
3
|
-
import { Envelope, Lock, SpinnerGap, User, WarningCircle } from "@phosphor-icons/react";
|
|
4
|
-
import * as React$1 from "react";
|
|
5
|
-
import { useForm } from "react-hook-form";
|
|
6
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
|
|
8
|
-
//#region src/client/views/auth/setup-form.tsx
|
|
9
|
-
/**
|
|
10
|
-
* Setup Form - create first admin account
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* Setup form for creating the first admin account.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* function SetupPage() {
|
|
18
|
-
* const [error, setError] = useState<string | null>(null)
|
|
19
|
-
*
|
|
20
|
-
* const handleSetup = async (values: SetupFormValues) => {
|
|
21
|
-
* const result = await client.rpc.createFirstAdmin({
|
|
22
|
-
* email: values.email,
|
|
23
|
-
* password: values.password,
|
|
24
|
-
* name: values.name,
|
|
25
|
-
* })
|
|
26
|
-
* if (!result.success) {
|
|
27
|
-
* setError(result.error)
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
*
|
|
31
|
-
* return (
|
|
32
|
-
* <AuthLayout title="Welcome">
|
|
33
|
-
* <SetupForm onSubmit={handleSetup} error={error} />
|
|
34
|
-
* </AuthLayout>
|
|
35
|
-
* )
|
|
36
|
-
* }
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
function SetupForm({ onSubmit, defaultValues, className, error }) {
|
|
40
|
-
const { register, handleSubmit, watch, formState: { errors, isSubmitting } } = useForm({ defaultValues: {
|
|
41
|
-
name: "",
|
|
42
|
-
email: "",
|
|
43
|
-
password: "",
|
|
44
|
-
confirmPassword: "",
|
|
45
|
-
...defaultValues
|
|
46
|
-
} });
|
|
47
|
-
const password = watch("password");
|
|
48
|
-
return /* @__PURE__ */ jsxs("form", {
|
|
49
|
-
onSubmit: handleSubmit(async (values) => {
|
|
50
|
-
await onSubmit(values);
|
|
51
|
-
}),
|
|
52
|
-
className: cn("space-y-4", className),
|
|
53
|
-
children: [
|
|
54
|
-
/* @__PURE__ */ jsxs(FieldGroup, { children: [
|
|
55
|
-
/* @__PURE__ */ jsxs(Field, {
|
|
56
|
-
"data-invalid": !!errors.name,
|
|
57
|
-
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
58
|
-
htmlFor: "name",
|
|
59
|
-
children: "Name"
|
|
60
|
-
}), /* @__PURE__ */ jsxs(FieldContent, { children: [/* @__PURE__ */ jsxs("div", {
|
|
61
|
-
className: "relative",
|
|
62
|
-
children: [/* @__PURE__ */ jsx(User, {
|
|
63
|
-
className: "text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2",
|
|
64
|
-
weight: "duotone"
|
|
65
|
-
}), /* @__PURE__ */ jsx(Input, {
|
|
66
|
-
id: "name",
|
|
67
|
-
type: "text",
|
|
68
|
-
placeholder: "Admin User",
|
|
69
|
-
className: "pl-8",
|
|
70
|
-
autoComplete: "name",
|
|
71
|
-
"aria-invalid": !!errors.name,
|
|
72
|
-
...register("name", {
|
|
73
|
-
required: "Name is required",
|
|
74
|
-
minLength: {
|
|
75
|
-
value: 2,
|
|
76
|
-
message: "Name must be at least 2 characters"
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
})]
|
|
80
|
-
}), /* @__PURE__ */ jsx(FieldError, { children: errors.name?.message })] })]
|
|
81
|
-
}),
|
|
82
|
-
/* @__PURE__ */ jsxs(Field, {
|
|
83
|
-
"data-invalid": !!errors.email,
|
|
84
|
-
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
85
|
-
htmlFor: "email",
|
|
86
|
-
children: "Email"
|
|
87
|
-
}), /* @__PURE__ */ jsxs(FieldContent, { children: [/* @__PURE__ */ jsxs("div", {
|
|
88
|
-
className: "relative",
|
|
89
|
-
children: [/* @__PURE__ */ jsx(Envelope, {
|
|
90
|
-
className: "text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2",
|
|
91
|
-
weight: "duotone"
|
|
92
|
-
}), /* @__PURE__ */ jsx(Input, {
|
|
93
|
-
id: "email",
|
|
94
|
-
type: "email",
|
|
95
|
-
placeholder: "admin@example.com",
|
|
96
|
-
className: "pl-8",
|
|
97
|
-
autoComplete: "email",
|
|
98
|
-
"aria-invalid": !!errors.email,
|
|
99
|
-
...register("email", {
|
|
100
|
-
required: "Email is required",
|
|
101
|
-
pattern: {
|
|
102
|
-
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
103
|
-
message: "Invalid email address"
|
|
104
|
-
}
|
|
105
|
-
})
|
|
106
|
-
})]
|
|
107
|
-
}), /* @__PURE__ */ jsx(FieldError, { children: errors.email?.message })] })]
|
|
108
|
-
}),
|
|
109
|
-
/* @__PURE__ */ jsxs(Field, {
|
|
110
|
-
"data-invalid": !!errors.password,
|
|
111
|
-
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
112
|
-
htmlFor: "password",
|
|
113
|
-
children: "Password"
|
|
114
|
-
}), /* @__PURE__ */ jsxs(FieldContent, { children: [/* @__PURE__ */ jsxs("div", {
|
|
115
|
-
className: "relative",
|
|
116
|
-
children: [/* @__PURE__ */ jsx(Lock, {
|
|
117
|
-
className: "text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2",
|
|
118
|
-
weight: "duotone"
|
|
119
|
-
}), /* @__PURE__ */ jsx(Input, {
|
|
120
|
-
id: "password",
|
|
121
|
-
type: "password",
|
|
122
|
-
placeholder: "Enter a secure password",
|
|
123
|
-
className: "pl-8",
|
|
124
|
-
autoComplete: "new-password",
|
|
125
|
-
"aria-invalid": !!errors.password,
|
|
126
|
-
...register("password", {
|
|
127
|
-
required: "Password is required",
|
|
128
|
-
minLength: {
|
|
129
|
-
value: 8,
|
|
130
|
-
message: "Password must be at least 8 characters"
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
})]
|
|
134
|
-
}), /* @__PURE__ */ jsx(FieldError, { children: errors.password?.message })] })]
|
|
135
|
-
}),
|
|
136
|
-
/* @__PURE__ */ jsxs(Field, {
|
|
137
|
-
"data-invalid": !!errors.confirmPassword,
|
|
138
|
-
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
139
|
-
htmlFor: "confirmPassword",
|
|
140
|
-
children: "Confirm Password"
|
|
141
|
-
}), /* @__PURE__ */ jsxs(FieldContent, { children: [/* @__PURE__ */ jsxs("div", {
|
|
142
|
-
className: "relative",
|
|
143
|
-
children: [/* @__PURE__ */ jsx(Lock, {
|
|
144
|
-
className: "text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2",
|
|
145
|
-
weight: "duotone"
|
|
146
|
-
}), /* @__PURE__ */ jsx(Input, {
|
|
147
|
-
id: "confirmPassword",
|
|
148
|
-
type: "password",
|
|
149
|
-
placeholder: "Confirm your password",
|
|
150
|
-
className: "pl-8",
|
|
151
|
-
autoComplete: "new-password",
|
|
152
|
-
"aria-invalid": !!errors.confirmPassword,
|
|
153
|
-
...register("confirmPassword", {
|
|
154
|
-
required: "Please confirm your password",
|
|
155
|
-
validate: (value) => value === password || "Passwords do not match"
|
|
156
|
-
})
|
|
157
|
-
})]
|
|
158
|
-
}), /* @__PURE__ */ jsx(FieldError, { children: errors.confirmPassword?.message })] })]
|
|
159
|
-
})
|
|
160
|
-
] }),
|
|
161
|
-
error && /* @__PURE__ */ jsxs(Alert, {
|
|
162
|
-
variant: "destructive",
|
|
163
|
-
children: [/* @__PURE__ */ jsx(WarningCircle, {}), /* @__PURE__ */ jsx(AlertDescription, { children: error })]
|
|
164
|
-
}),
|
|
165
|
-
/* @__PURE__ */ jsx(Button, {
|
|
166
|
-
type: "submit",
|
|
167
|
-
className: "w-full",
|
|
168
|
-
size: "lg",
|
|
169
|
-
disabled: isSubmitting,
|
|
170
|
-
children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SpinnerGap, {
|
|
171
|
-
className: "animate-spin",
|
|
172
|
-
weight: "bold"
|
|
173
|
-
}), "Creating account..."] }) : "Create Admin Account"
|
|
174
|
-
})
|
|
175
|
-
]
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/client/views/pages/setup-page.tsx
|
|
181
|
-
/**
|
|
182
|
-
* Setup Page
|
|
183
|
-
*
|
|
184
|
-
* Default setup page for creating the first admin account.
|
|
185
|
-
* Uses AuthLayout and SetupForm, integrates with client from AdminProvider context.
|
|
186
|
-
*/
|
|
187
|
-
/**
|
|
188
|
-
* Default setup page component.
|
|
189
|
-
*
|
|
190
|
-
* Uses client from AdminProvider to call createFirstAdmin RPC function.
|
|
191
|
-
*
|
|
192
|
-
* @example
|
|
193
|
-
* ```tsx
|
|
194
|
-
* // In your admin config
|
|
195
|
-
* const admin = qa<AppCMS>()
|
|
196
|
-
* .use(coreAdminModule)
|
|
197
|
-
* .pages({
|
|
198
|
-
* setup: page("setup", { component: SetupPage }).path("/setup"),
|
|
199
|
-
* })
|
|
200
|
-
* ```
|
|
201
|
-
*/
|
|
202
|
-
function SetupPage({ title = "Welcome", description = "Create your admin account to get started", logo, redirectTo, loginPath, showLoginLink = true }) {
|
|
203
|
-
const client = useAdminStore(selectClient);
|
|
204
|
-
const navigate = useAdminStore(selectNavigate);
|
|
205
|
-
const basePath = useAdminStore(selectBasePath);
|
|
206
|
-
const brandName = useAdminStore(selectBrandName);
|
|
207
|
-
const [error, setError] = React$1.useState(null);
|
|
208
|
-
const handleSubmit = async (values) => {
|
|
209
|
-
setError(null);
|
|
210
|
-
try {
|
|
211
|
-
const result = await client.functions.createFirstAdmin({
|
|
212
|
-
email: values.email,
|
|
213
|
-
password: values.password,
|
|
214
|
-
name: values.name
|
|
215
|
-
});
|
|
216
|
-
if (!result.success) {
|
|
217
|
-
setError(result.error ?? "Failed to create admin account");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
navigate(redirectTo ?? `${basePath}/login`);
|
|
221
|
-
} catch (err) {
|
|
222
|
-
setError(err instanceof Error ? err.message : "An error occurred");
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const handleLoginClick = () => {
|
|
226
|
-
navigate(loginPath ?? `${basePath}/login`);
|
|
227
|
-
};
|
|
228
|
-
return /* @__PURE__ */ jsx(AuthLayout, {
|
|
229
|
-
title,
|
|
230
|
-
description,
|
|
231
|
-
logo: logo ?? /* @__PURE__ */ jsx(DefaultLogo, { brandName }),
|
|
232
|
-
footer: showLoginLink && /* @__PURE__ */ jsxs("p", {
|
|
233
|
-
className: "text-muted-foreground text-center text-xs",
|
|
234
|
-
children: [
|
|
235
|
-
"Already have an account?",
|
|
236
|
-
" ",
|
|
237
|
-
/* @__PURE__ */ jsx("button", {
|
|
238
|
-
type: "button",
|
|
239
|
-
onClick: handleLoginClick,
|
|
240
|
-
className: "text-primary hover:underline",
|
|
241
|
-
children: "Sign in"
|
|
242
|
-
})
|
|
243
|
-
]
|
|
244
|
-
}),
|
|
245
|
-
children: /* @__PURE__ */ jsx(SetupForm, {
|
|
246
|
-
onSubmit: handleSubmit,
|
|
247
|
-
error
|
|
248
|
-
})
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
function DefaultLogo({ brandName }) {
|
|
252
|
-
return /* @__PURE__ */ jsx("div", {
|
|
253
|
-
className: "text-center",
|
|
254
|
-
children: /* @__PURE__ */ jsx("h1", {
|
|
255
|
-
className: "text-xl font-bold",
|
|
256
|
-
children: brandName
|
|
257
|
-
})
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
var setup_page_default = SetupPage;
|
|
261
|
-
|
|
262
|
-
//#endregion
|
|
263
|
-
export { setup_page_default as n, SetupForm as r, SetupPage as t };
|
|
264
|
-
//# sourceMappingURL=setup-page-YAP_fzqh.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setup-page-YAP_fzqh.mjs","names":["React"],"sources":["../src/client/views/auth/setup-form.tsx","../src/client/views/pages/setup-page.tsx"],"sourcesContent":["/**\n * Setup Form - create first admin account\n */\n\nimport {\n Envelope,\n Lock,\n SpinnerGap,\n User,\n WarningCircle,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { Alert, AlertDescription } from \"../../components/ui/alert\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Field,\n FieldContent,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"../../components/ui/field\";\nimport { Input } from \"../../components/ui/input\";\nimport { cn } from \"../../lib/utils\";\n\nexport type SetupFormValues = {\n name: string;\n email: string;\n password: string;\n confirmPassword: string;\n};\n\nexport type SetupFormProps = {\n /** Called when form is submitted with valid data */\n onSubmit: (values: SetupFormValues) => Promise<void>;\n /** Default values */\n defaultValues?: Partial<SetupFormValues>;\n /** Additional class name */\n className?: string;\n /** Error message from setup */\n error?: string | null;\n};\n\n/**\n * Setup form for creating the first admin account.\n *\n * @example\n * ```tsx\n * function SetupPage() {\n * const [error, setError] = useState<string | null>(null)\n *\n * const handleSetup = async (values: SetupFormValues) => {\n * const result = await client.rpc.createFirstAdmin({\n * email: values.email,\n * password: values.password,\n * name: values.name,\n * })\n * if (!result.success) {\n * setError(result.error)\n * }\n * }\n *\n * return (\n * <AuthLayout title=\"Welcome\">\n * <SetupForm onSubmit={handleSetup} error={error} />\n * </AuthLayout>\n * )\n * }\n * ```\n */\nexport function SetupForm({\n onSubmit,\n defaultValues,\n className,\n error,\n}: SetupFormProps) {\n const {\n register,\n handleSubmit,\n watch,\n formState: { errors, isSubmitting },\n } = useForm<SetupFormValues>({\n defaultValues: {\n name: \"\",\n email: \"\",\n password: \"\",\n confirmPassword: \"\",\n ...defaultValues,\n },\n });\n\n const password = watch(\"password\");\n\n const handleFormSubmit = handleSubmit(async (values) => {\n await onSubmit(values);\n });\n\n return (\n <form onSubmit={handleFormSubmit} className={cn(\"space-y-4\", className)}>\n <FieldGroup>\n {/* Name Field */}\n <Field data-invalid={!!errors.name}>\n <FieldLabel htmlFor=\"name\">Name</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <User\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"name\"\n type=\"text\"\n placeholder=\"Admin User\"\n className=\"pl-8\"\n autoComplete=\"name\"\n aria-invalid={!!errors.name}\n {...register(\"name\", {\n required: \"Name is required\",\n minLength: {\n value: 2,\n message: \"Name must be at least 2 characters\",\n },\n })}\n />\n </div>\n <FieldError>{errors.name?.message}</FieldError>\n </FieldContent>\n </Field>\n\n {/* Email Field */}\n <Field data-invalid={!!errors.email}>\n <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <Envelope\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"admin@example.com\"\n className=\"pl-8\"\n autoComplete=\"email\"\n aria-invalid={!!errors.email}\n {...register(\"email\", {\n required: \"Email is required\",\n pattern: {\n value: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,\n message: \"Invalid email address\",\n },\n })}\n />\n </div>\n <FieldError>{errors.email?.message}</FieldError>\n </FieldContent>\n </Field>\n\n {/* Password Field */}\n <Field data-invalid={!!errors.password}>\n <FieldLabel htmlFor=\"password\">Password</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <Lock\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"password\"\n type=\"password\"\n placeholder=\"Enter a secure password\"\n className=\"pl-8\"\n autoComplete=\"new-password\"\n aria-invalid={!!errors.password}\n {...register(\"password\", {\n required: \"Password is required\",\n minLength: {\n value: 8,\n message: \"Password must be at least 8 characters\",\n },\n })}\n />\n </div>\n <FieldError>{errors.password?.message}</FieldError>\n </FieldContent>\n </Field>\n\n {/* Confirm Password Field */}\n <Field data-invalid={!!errors.confirmPassword}>\n <FieldLabel htmlFor=\"confirmPassword\">Confirm Password</FieldLabel>\n <FieldContent>\n <div className=\"relative\">\n <Lock\n className=\"text-muted-foreground absolute left-2 top-1/2 size-4 -translate-y-1/2\"\n weight=\"duotone\"\n />\n <Input\n id=\"confirmPassword\"\n type=\"password\"\n placeholder=\"Confirm your password\"\n className=\"pl-8\"\n autoComplete=\"new-password\"\n aria-invalid={!!errors.confirmPassword}\n {...register(\"confirmPassword\", {\n required: \"Please confirm your password\",\n validate: (value) =>\n value === password || \"Passwords do not match\",\n })}\n />\n </div>\n <FieldError>{errors.confirmPassword?.message}</FieldError>\n </FieldContent>\n </Field>\n </FieldGroup>\n\n {/* Error Message */}\n {error && (\n <Alert variant=\"destructive\">\n <WarningCircle />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n\n {/* Submit Button */}\n <Button\n type=\"submit\"\n className=\"w-full\"\n size=\"lg\"\n disabled={isSubmitting}\n >\n {isSubmitting ? (\n <>\n <SpinnerGap className=\"animate-spin\" weight=\"bold\" />\n Creating account...\n </>\n ) : (\n \"Create Admin Account\"\n )}\n </Button>\n </form>\n );\n}\n","/**\n * Setup Page\n *\n * Default setup page for creating the first admin account.\n * Uses AuthLayout and SetupForm, integrates with client from AdminProvider context.\n */\n\nimport * as React from \"react\";\nimport {\n selectBasePath,\n selectBrandName,\n selectClient,\n selectNavigate,\n useAdminStore,\n} from \"../../runtime/provider\";\nimport { AuthLayout } from \"../auth/auth-layout\";\nimport { SetupForm, type SetupFormValues } from \"../auth/setup-form\";\n\nexport interface SetupPageProps {\n /**\n * Title shown on the setup page\n * @default \"Welcome\"\n */\n title?: string;\n\n /**\n * Description shown below the title\n * @default \"Create your admin account to get started\"\n */\n description?: string;\n\n /**\n * Logo component to show above the form\n */\n logo?: React.ReactNode;\n\n /**\n * Path to redirect after successful setup\n * @default \"{basePath}/login\"\n */\n redirectTo?: string;\n\n /**\n * Path to login page (shown in footer)\n * @default \"{basePath}/login\"\n */\n loginPath?: string;\n\n /**\n * Show \"Already have an account?\" link\n * @default true\n */\n showLoginLink?: boolean;\n}\n\n/**\n * Default setup page component.\n *\n * Uses client from AdminProvider to call createFirstAdmin RPC function.\n *\n * @example\n * ```tsx\n * // In your admin config\n * const admin = qa<AppCMS>()\n * .use(coreAdminModule)\n * .pages({\n * setup: page(\"setup\", { component: SetupPage }).path(\"/setup\"),\n * })\n * ```\n */\nexport function SetupPage({\n title = \"Welcome\",\n description = \"Create your admin account to get started\",\n logo,\n redirectTo,\n loginPath,\n showLoginLink = true,\n}: SetupPageProps) {\n const client = useAdminStore(selectClient);\n const navigate = useAdminStore(selectNavigate);\n const basePath = useAdminStore(selectBasePath);\n const brandName = useAdminStore(selectBrandName);\n\n const [error, setError] = React.useState<string | null>(null);\n\n const handleSubmit = async (values: SetupFormValues) => {\n setError(null);\n\n try {\n const result = await (client as any).functions.createFirstAdmin({\n email: values.email,\n password: values.password,\n name: values.name,\n });\n\n if (!result.success) {\n setError(result.error ?? \"Failed to create admin account\");\n return;\n }\n\n // Redirect to login on success\n navigate(redirectTo ?? `${basePath}/login`);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"An error occurred\");\n }\n };\n\n const handleLoginClick = () => {\n navigate(loginPath ?? `${basePath}/login`);\n };\n\n return (\n <AuthLayout\n title={title}\n description={description}\n logo={logo ?? <DefaultLogo brandName={brandName} />}\n footer={\n showLoginLink && (\n <p className=\"text-muted-foreground text-center text-xs\">\n Already have an account?{\" \"}\n <button\n type=\"button\"\n onClick={handleLoginClick}\n className=\"text-primary hover:underline\"\n >\n Sign in\n </button>\n </p>\n )\n }\n >\n <SetupForm onSubmit={handleSubmit} error={error} />\n </AuthLayout>\n );\n}\n\nfunction DefaultLogo({ brandName }: { brandName: string }) {\n return (\n <div className=\"text-center\">\n <h1 className=\"text-xl font-bold\">{brandName}</h1>\n </div>\n );\n}\n\nexport default SetupPage;\n"],"mappings":"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"}
|
package/dist/shared.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.mts","names":[],"sources":["../src/shared/preview-utils.ts"],"sourcesContent":[],"mappings":";;;;;;;AAeA;AAoBA;AAqBA;AAcA;;;;cAvDa,iBAAA;;;;;;;;;;;;;;;iBAoBG,WAAA;;;;;;;;;;;;;;;;;iBAqBA,qBAAA;;;;;;;iBAcA,gBAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-auth-BoLmWtmU.mjs","names":[],"sources":["../src/client/hooks/use-auth.ts"],"sourcesContent":["/**\n * Auth client utilities for admin package\n *\n * Better Auth handles all auth state management internally.\n * This module provides type-safe client creation helpers with\n * full type inference from your CMS auth configuration.\n *\n * @example\n * ```tsx\n * // In your app, create the auth client once:\n * import { createAdminAuthClient } from '@questpie/admin/hooks'\n * import type { cms } from './server/cms'\n *\n * export const authClient = createAdminAuthClient<typeof cms>({\n * baseURL: 'http://localhost:3000'\n * })\n *\n * // Then use the built-in hooks:\n * function MyComponent() {\n * const { data: session, isPending, error } = authClient.useSession()\n *\n * if (isPending) return <div>Loading...</div>\n * if (!session) return <LoginForm />\n *\n * return <div>Welcome {session.user.name}</div>\n * }\n * ```\n */\n\nimport type { BetterAuthOptions } from \"better-auth\";\nimport { createAuthClient } from \"better-auth/react\";\nimport type { Questpie } from \"questpie\";\n\n/**\n * Extract auth options type from Questpie instance\n */\ntype ExtractAuthOptions<T extends Questpie<any>> =\n T extends Questpie<infer TConfig>\n ? TConfig[\"auth\"] extends BetterAuthOptions\n ? TConfig[\"auth\"]\n : BetterAuthOptions\n : BetterAuthOptions;\n\n/**\n * Options for creating admin auth client\n */\nexport type AdminAuthClientOptions = {\n /**\n * Base URL of the CMS API\n * @example 'http://localhost:3000'\n */\n baseURL: string;\n\n /**\n * Base path for auth routes\n * @default '/cms/auth'\n */\n basePath?: string;\n\n /**\n * Custom fetch implementation\n */\n fetchOptions?: {\n credentials?: RequestCredentials;\n headers?: Record<string, string>;\n };\n};\n\n/**\n * Internal client options type that includes $InferAuth\n */\ntype InternalClientOptions<T extends Questpie<any>> = {\n baseURL: string;\n fetchOptions?: {\n credentials?: RequestCredentials;\n headers?: Record<string, string>;\n };\n $InferAuth: ExtractAuthOptions<T>;\n};\n\n/**\n * Create a type-safe Better Auth client for the admin UI\n *\n * The returned client includes full type inference from your CMS auth configuration:\n * - `useSession()` - React hook for session state (typed based on your user/session schema)\n * - `signIn` methods (email, social providers configured in CMS)\n * - `signOut` - Sign out the current user\n * - `signUp` - Register new users\n * - Plugin-specific hooks (e.g., `useListAccounts` for admin plugin)\n *\n * @example\n * ```tsx\n * import { createAdminAuthClient } from '@questpie/admin/hooks'\n * import type { cms } from './server/cms'\n *\n * // Create client with type inference from your CMS\n * export const authClient = createAdminAuthClient<typeof cms>({\n * baseURL: 'http://localhost:3000'\n * })\n *\n * // Session data is fully typed based on your CMS auth config\n * function App() {\n * const { data: session, isPending } = authClient.useSession()\n *\n * if (isPending) return <Loading />\n * if (!session) return <LoginPage authClient={authClient} />\n *\n * // session.user has all fields from your user schema\n * return <AdminDashboard user={session.user} />\n * }\n * ```\n */\nexport function createAdminAuthClient<T extends Questpie<any>>(\n options: AdminAuthClientOptions,\n): ReturnType<typeof createAuthClient<InternalClientOptions<T>>> {\n const basePath = options.basePath ?? \"/cms/auth\";\n\n return createAuthClient<InternalClientOptions<T>>({\n baseURL: `${options.baseURL}${basePath}`,\n fetchOptions: {\n credentials: options.fetchOptions?.credentials ?? \"include\",\n headers: options.fetchOptions?.headers,\n },\n } as InternalClientOptions<T>);\n}\n\n/**\n * Type helper to extract auth client type from CMS instance\n *\n * @example\n * ```tsx\n * import type { AdminAuthClient } from '@questpie/admin/hooks'\n * import type { cms } from './server/cms'\n *\n * type MyAuthClient = AdminAuthClient<typeof cms>\n * ```\n */\nexport type AdminAuthClient<T extends Questpie<any>> = ReturnType<\n typeof createAdminAuthClient<T>\n>;\n\n/**\n * Type helper to extract session type from auth client\n *\n * @example\n * ```tsx\n * import type { AdminSession } from '@questpie/admin/hooks'\n * import type { cms } from './server/cms'\n *\n * type MySession = AdminSession<typeof cms>\n * // Includes: { user: { id, email, name, role, ... }, session: { ... } }\n * ```\n */\nexport type AdminSession<T extends Questpie<any>> =\n AdminAuthClient<T>[\"$Infer\"][\"Session\"];\n\n/**\n * Type helper to extract user type from CMS auth configuration\n *\n * @example\n * ```tsx\n * import type { AdminUser } from '@questpie/admin/hooks'\n * import type { cms } from './server/cms'\n *\n * type MyUser = AdminUser<typeof cms>\n * ```\n */\nexport type AdminUser<T extends Questpie<any>> = AdminSession<T>[\"user\"];\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\nimport { selectAuthClient, useAdminStore } from \"../runtime/provider\";\n\n/**\n * Hook to access the auth client from AdminProvider context.\n *\n * @example\n * ```tsx\n * import { useAuthClient } from '@questpie/admin/hooks'\n *\n * function LoginPage() {\n * const authClient = useAuthClient()\n *\n * const handleLogin = async (email: string, password: string) => {\n * await authClient.signIn.email({ email, password })\n * }\n *\n * return <LoginForm onSubmit={handleLogin} />\n * }\n * ```\n */\nexport function useAuthClient<T = any>(): T {\n const authClient = useAdminStore(selectAuthClient);\n if (!authClient) {\n throw new Error(\n \"useAuthClient: authClient is not provided. \" +\n \"Make sure to pass authClient to AdminProvider.\",\n );\n }\n return authClient as T;\n}\n\n/**\n * Hook to access the auth client from AdminProvider context.\n * Returns null if authClient is not provided (safe version).\n *\n * @example\n * ```tsx\n * const authClient = useAuthClientSafe()\n * if (!authClient) {\n * return <div>Auth not configured</div>\n * }\n * ```\n */\nexport function useAuthClientSafe<T = any>(): T | null {\n return useAdminStore(selectAuthClient) as T | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgHA,SAAgB,sBACd,SAC+D;CAC/D,MAAM,WAAW,QAAQ,YAAY;AAErC,QAAO,iBAA2C;EAChD,SAAS,GAAG,QAAQ,UAAU;EAC9B,cAAc;GACZ,aAAa,QAAQ,cAAc,eAAe;GAClD,SAAS,QAAQ,cAAc;GAChC;EACF,CAA6B;;;;;;;;;;;;;;;;;;;;AAsEhC,SAAgB,gBAA4B;CAC1C,MAAM,aAAa,cAAc,iBAAiB;AAClD,KAAI,CAAC,WACH,OAAM,IAAI,MACR,4FAED;AAEH,QAAO;;;;;;;;;;;;;;AAeT,SAAgB,oBAAuC;AACrD,QAAO,cAAc,iBAAiB"}
|