@rebasepro/studio 0.0.1-canary.09e5ec5
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/LICENSE +114 -0
- package/README.md +159 -0
- package/dist/ApiExplorer-gMJt5JrS.js +1053 -0
- package/dist/ApiExplorer-gMJt5JrS.js.map +1 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js +118 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js.map +1 -0
- package/dist/BranchesView-DcHZtvXo.js +292 -0
- package/dist/BranchesView-DcHZtvXo.js.map +1 -0
- package/dist/CronJobsView-CijCToeK.js +456 -0
- package/dist/CronJobsView-CijCToeK.js.map +1 -0
- package/dist/JSEditor-D8nVp3Lp.js +1308 -0
- package/dist/JSEditor-D8nVp3Lp.js.map +1 -0
- package/dist/MonacoEditor-CMYEjiRf.js +161 -0
- package/dist/MonacoEditor-CMYEjiRf.js.map +1 -0
- package/dist/RLSEditor-DBH09u9v.js +1831 -0
- package/dist/RLSEditor-DBH09u9v.js.map +1 -0
- package/dist/SQLEditor-CkVx9vgr.js +1792 -0
- package/dist/SQLEditor-CkVx9vgr.js.map +1 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js +1069 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js.map +1 -0
- package/dist/StorageView-CTqGFhY9.js +907 -0
- package/dist/StorageView-CTqGFhY9.js.map +1 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/core/src/components/AIIcon.d.ts +16 -0
- package/dist/core/src/components/ConfirmationDialog.d.ts +9 -0
- package/dist/core/src/components/Debug/UIReferenceView.d.ts +1 -0
- package/dist/core/src/components/Debug/UIStyleGuide.d.ts +1 -0
- package/dist/core/src/components/ErrorTooltip.d.ts +2 -0
- package/dist/core/src/components/ErrorView.d.ts +21 -0
- package/dist/core/src/components/LanguageToggle.d.ts +1 -0
- package/dist/core/src/components/LoginView/LoginView.d.ts +68 -0
- package/dist/core/src/components/LoginView/index.d.ts +2 -0
- package/dist/core/src/components/NotFoundPage.d.ts +1 -0
- package/dist/core/src/components/RebaseAuth.d.ts +10 -0
- package/dist/core/src/components/RebaseLogo.d.ts +7 -0
- package/dist/core/src/components/UnsavedChangesDialog.d.ts +9 -0
- package/dist/core/src/components/UserDisplay.d.ts +7 -0
- package/dist/core/src/components/UserSelectPopover.d.ts +62 -0
- package/dist/core/src/components/UserSettingsView.d.ts +1 -0
- package/dist/core/src/components/common/index.d.ts +6 -0
- package/dist/core/src/components/common/table_height.d.ts +5 -0
- package/dist/core/src/components/common/types.d.ts +63 -0
- package/dist/core/src/components/common/useColumnsIds.d.ts +9 -0
- package/dist/core/src/components/common/useDataTableController.d.ts +45 -0
- package/dist/core/src/components/common/useDebouncedData.d.ts +9 -0
- package/dist/core/src/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/core/src/components/index.d.ts +16 -0
- package/dist/core/src/contexts/AdminModeController.d.ts +4 -0
- package/dist/core/src/contexts/AnalyticsContext.d.ts +3 -0
- package/dist/core/src/contexts/AuthControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/CustomizationControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/DataDriverContext.d.ts +3 -0
- package/dist/core/src/contexts/DatabaseAdminContext.d.ts +3 -0
- package/dist/core/src/contexts/DialogsProvider.d.ts +4 -0
- package/dist/core/src/contexts/EffectiveRoleController.d.ts +4 -0
- package/dist/core/src/contexts/InternalUserManagementContext.d.ts +3 -0
- package/dist/core/src/contexts/ModeController.d.ts +4 -0
- package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +6 -0
- package/dist/core/src/contexts/RebaseDataContext.d.ts +3 -0
- package/dist/core/src/contexts/SnackbarProvider.d.ts +2 -0
- package/dist/core/src/contexts/StorageSourceContext.d.ts +3 -0
- package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
- package/dist/core/src/contexts/index.d.ts +13 -0
- package/dist/core/src/core/PluginLifecycleManager.d.ts +17 -0
- package/dist/core/src/core/PluginProviderStack.d.ts +21 -0
- package/dist/core/src/core/Rebase.d.ts +14 -0
- package/dist/core/src/core/RebaseProps.d.ts +136 -0
- package/dist/core/src/core/RebaseRouter.d.ts +4 -0
- package/dist/core/src/core/RebaseRoutes.d.ts +17 -0
- package/dist/core/src/core/index.d.ts +4 -0
- package/dist/core/src/hooks/ApiConfigContext.d.ts +24 -0
- package/dist/core/src/hooks/data/delete.d.ts +31 -0
- package/dist/core/src/hooks/data/save.d.ts +34 -0
- package/dist/core/src/hooks/data/useCollectionFetch.d.ts +51 -0
- package/dist/core/src/hooks/data/useData.d.ts +13 -0
- package/dist/core/src/hooks/data/useDataOrder.d.ts +12 -0
- package/dist/core/src/hooks/data/useEntityFetch.d.ts +38 -0
- package/dist/core/src/hooks/data/useRelationSelector.d.ts +52 -0
- package/dist/core/src/hooks/data/useUserSelector.d.ts +31 -0
- package/dist/core/src/hooks/index.d.ts +37 -0
- package/dist/core/src/hooks/useAdminModeController.d.ts +19 -0
- package/dist/core/src/hooks/useAnalyticsController.d.ts +5 -0
- package/dist/core/src/hooks/useAuthController.d.ts +11 -0
- package/dist/core/src/hooks/useAuthSubscription.d.ts +2 -0
- package/dist/core/src/hooks/useBackendStorageSource.d.ts +30 -0
- package/dist/core/src/hooks/useBridgeRegistration.d.ts +18 -0
- package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +6 -0
- package/dist/core/src/hooks/useBuildAdminModeController.d.ts +6 -0
- package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +8 -0
- package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
- package/dist/core/src/hooks/useBuildModeController.d.ts +6 -0
- package/dist/core/src/hooks/useClipboard.d.ts +57 -0
- package/dist/core/src/hooks/useCollapsedGroups.d.ts +12 -0
- package/dist/core/src/hooks/useCustomizationController.d.ts +11 -0
- package/dist/core/src/hooks/useDialogsController.d.ts +11 -0
- package/dist/core/src/hooks/useEffectiveRoleController.d.ts +7 -0
- package/dist/core/src/hooks/useInternalUserManagementController.d.ts +12 -0
- package/dist/core/src/hooks/useLargeLayout.d.ts +1 -0
- package/dist/core/src/hooks/useModeController.d.ts +19 -0
- package/dist/core/src/hooks/usePermissions.d.ts +12 -0
- package/dist/core/src/hooks/useRebaseClient.d.ts +5 -0
- package/dist/core/src/hooks/useRebaseContext.d.ts +11 -0
- package/dist/core/src/hooks/useRebaseRegistry.d.ts +34 -0
- package/dist/core/src/hooks/useSlot.d.ts +18 -0
- package/dist/core/src/hooks/useSnackbarController.d.ts +20 -0
- package/dist/core/src/hooks/useStorageSource.d.ts +7 -0
- package/dist/core/src/hooks/useStudioBridge.d.ts +91 -0
- package/dist/core/src/hooks/useTranslation.d.ts +17 -0
- package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +12 -0
- package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +8 -0
- package/dist/core/src/hooks/useValidateAuthenticator.d.ts +21 -0
- package/dist/core/src/i18n/RebaseI18nProvider.d.ts +33 -0
- package/dist/core/src/index.d.ts +15 -0
- package/dist/core/src/internal/common.d.ts +3 -0
- package/dist/core/src/internal/useRestoreScroll.d.ts +6 -0
- package/dist/core/src/locales/de.d.ts +2 -0
- package/dist/core/src/locales/en.d.ts +10 -0
- package/dist/core/src/locales/es.d.ts +10 -0
- package/dist/core/src/locales/fr.d.ts +2 -0
- package/dist/core/src/locales/hi.d.ts +2 -0
- package/dist/core/src/locales/it.d.ts +2 -0
- package/dist/core/src/locales/pt.d.ts +7 -0
- package/dist/core/src/util/constants.d.ts +1 -0
- package/dist/core/src/util/createFormexStub.d.ts +2 -0
- package/dist/core/src/util/entity_cache.d.ts +27 -0
- package/dist/core/src/util/enums.d.ts +5 -0
- package/dist/core/src/util/icon_list.d.ts +5 -0
- package/dist/core/src/util/icon_synonyms.d.ts +1 -0
- package/dist/core/src/util/icons.d.ts +20 -0
- package/dist/core/src/util/index.d.ts +10 -0
- package/dist/core/src/util/previews.d.ts +4 -0
- package/dist/core/src/util/useStorageUploadController.d.ts +38 -0
- package/dist/core/src/util/useTraceUpdate.d.ts +2 -0
- package/dist/formex/src/Field.d.ts +52 -0
- package/dist/formex/src/Formex.d.ts +7 -0
- package/dist/formex/src/index.d.ts +5 -0
- package/dist/formex/src/types.d.ts +40 -0
- package/dist/formex/src/useCreateFormex.d.ts +14 -0
- package/dist/formex/src/utils.d.ts +16 -0
- package/dist/index.es.js +726 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +9647 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/studio/src/components/ApiExplorer/ApiExplorer.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/EndpointDetail.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/TryItPanel.d.ts +15 -0
- package/dist/studio/src/components/ApiExplorer/parseSpec.d.ts +16 -0
- package/dist/studio/src/components/ApiExplorer/types.d.ts +90 -0
- package/dist/studio/src/components/AuthSimulationSelector.d.ts +11 -0
- package/dist/studio/src/components/Branches/BranchesView.d.ts +1 -0
- package/dist/studio/src/components/CronJobs/CronJobsView.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditor.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/JSEditor/JSMonacoEditor.d.ts +18 -0
- package/dist/studio/src/components/RLSEditor/PolicyEditor.d.ts +9 -0
- package/dist/studio/src/components/RLSEditor/RLSEditor.d.ts +19 -0
- package/dist/studio/src/components/RLSEditor/index.d.ts +1 -0
- package/dist/studio/src/components/RebaseStudio.d.ts +2 -0
- package/dist/studio/src/components/SQLEditor/ExplainVisualizer.d.ts +24 -0
- package/dist/studio/src/components/SQLEditor/MonacoEditor.d.ts +17 -0
- package/dist/studio/src/components/SQLEditor/SQLEditor.d.ts +11 -0
- package/dist/studio/src/components/SQLEditor/SQLEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/SQLEditor/SchemaBrowser.d.ts +8 -0
- package/dist/studio/src/components/SchemaVisualizer/RelationEdge.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/SchemaVisualizer.d.ts +2 -0
- package/dist/studio/src/components/SchemaVisualizer/TableNode.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/index.d.ts +5 -0
- package/dist/studio/src/components/SchemaVisualizer/schema-visualizer.utils.d.ts +42 -0
- package/dist/studio/src/components/SchemaVisualizer/useSchemaGraph.d.ts +37 -0
- package/dist/studio/src/components/StorageView/StorageView.d.ts +1 -0
- package/dist/studio/src/components/StudioHomePage.d.ts +9 -0
- package/dist/studio/src/index.d.ts +4 -0
- package/dist/studio/src/utils/entities.d.ts +0 -0
- package/dist/studio/src/utils/pgColumnToProperty.d.ts +6 -0
- package/dist/studio/src/utils/sql_utils.d.ts +52 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/dist/ui/src/components/Alert.d.ts +12 -0
- package/dist/ui/src/components/Autocomplete.d.ts +21 -0
- package/dist/ui/src/components/Avatar.d.ts +11 -0
- package/dist/ui/src/components/Badge.d.ts +8 -0
- package/dist/ui/src/components/BooleanSwitch.d.ts +14 -0
- package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +17 -0
- package/dist/ui/src/components/Button.d.ts +14 -0
- package/dist/ui/src/components/Card.d.ts +9 -0
- package/dist/ui/src/components/CenteredView.d.ts +9 -0
- package/dist/ui/src/components/Checkbox.d.ts +13 -0
- package/dist/ui/src/components/Chip.d.ts +26 -0
- package/dist/ui/src/components/CircularProgress.d.ts +5 -0
- package/dist/ui/src/components/CircularProgressCenter.d.ts +11 -0
- package/dist/ui/src/components/Collapse.d.ts +9 -0
- package/dist/ui/src/components/ColorPicker.d.ts +30 -0
- package/dist/ui/src/components/Container.d.ts +8 -0
- package/dist/ui/src/components/DateTimeField.d.ts +24 -0
- package/dist/ui/src/components/DebouncedTextField.d.ts +2 -0
- package/dist/ui/src/components/Dialog.d.ts +39 -0
- package/dist/ui/src/components/DialogActions.d.ts +7 -0
- package/dist/ui/src/components/DialogContent.d.ts +7 -0
- package/dist/ui/src/components/DialogTitle.d.ts +10 -0
- package/dist/ui/src/components/ErrorBoundary.d.ts +11 -0
- package/dist/ui/src/components/ExpandablePanel.d.ts +12 -0
- package/dist/ui/src/components/FileUpload.d.ts +23 -0
- package/dist/ui/src/components/IconButton.d.ts +12 -0
- package/dist/ui/src/components/InfoLabel.d.ts +5 -0
- package/dist/ui/src/components/InputLabel.d.ts +11 -0
- package/dist/ui/src/components/Label.d.ts +7 -0
- package/dist/ui/src/components/LoadingButton.d.ts +7 -0
- package/dist/ui/src/components/Markdown.d.ts +10 -0
- package/dist/ui/src/components/Menu.d.ts +23 -0
- package/dist/ui/src/components/Menubar.d.ts +80 -0
- package/dist/ui/src/components/MultiSelect.d.ts +48 -0
- package/dist/ui/src/components/Paper.d.ts +6 -0
- package/dist/ui/src/components/Popover.d.ts +24 -0
- package/dist/ui/src/components/RadioGroup.d.ts +28 -0
- package/dist/ui/src/components/ResizablePanels.d.ts +18 -0
- package/dist/ui/src/components/SearchBar.d.ts +22 -0
- package/dist/ui/src/components/Select.d.ts +43 -0
- package/dist/ui/src/components/Separator.d.ts +5 -0
- package/dist/ui/src/components/Sheet.d.ts +22 -0
- package/dist/ui/src/components/Skeleton.d.ts +6 -0
- package/dist/ui/src/components/Slider.d.ts +21 -0
- package/dist/ui/src/components/Table.d.ts +34 -0
- package/dist/ui/src/components/Tabs.d.ts +19 -0
- package/dist/ui/src/components/TextField.d.ts +58 -0
- package/dist/ui/src/components/TextareaAutosize.d.ts +43 -0
- package/dist/ui/src/components/ToggleButtonGroup.d.ts +30 -0
- package/dist/ui/src/components/Tooltip.d.ts +19 -0
- package/dist/ui/src/components/Typography.d.ts +36 -0
- package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +11 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +21 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +29 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +2 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +243 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/index.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/types.d.ts +38 -0
- package/dist/ui/src/components/common/SelectInputLabel.d.ts +5 -0
- package/dist/ui/src/components/index.d.ts +53 -0
- package/dist/ui/src/hooks/PortalContainerContext.d.ts +31 -0
- package/dist/ui/src/hooks/index.d.ts +6 -0
- package/dist/ui/src/hooks/useDebounceCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useDebounceValue.d.ts +1 -0
- package/dist/ui/src/hooks/useDebouncedCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useInjectStyles.d.ts +7 -0
- package/dist/ui/src/hooks/useOutsideAlerter.d.ts +5 -0
- package/dist/ui/src/icons/GitHubIcon.d.ts +2 -0
- package/dist/ui/src/icons/HandleIcon.d.ts +1 -0
- package/dist/ui/src/icons/Icon.d.ts +20 -0
- package/dist/ui/src/icons/cool_icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/index.d.ts +6 -0
- package/dist/ui/src/index.d.ts +5 -0
- package/dist/ui/src/styles.d.ts +12 -0
- package/dist/ui/src/util/chip_colors.d.ts +4 -0
- package/dist/ui/src/util/cls.d.ts +2 -0
- package/dist/ui/src/util/debounce.d.ts +10 -0
- package/dist/ui/src/util/hash.d.ts +1 -0
- package/dist/ui/src/util/index.d.ts +4 -0
- package/dist/ui/src/util/key_to_icon_component.d.ts +1 -0
- package/package.json +84 -0
- package/src/components/ApiExplorer/ApiExplorer.tsx +290 -0
- package/src/components/ApiExplorer/EndpointDetail.tsx +271 -0
- package/src/components/ApiExplorer/TryItPanel.tsx +510 -0
- package/src/components/ApiExplorer/parseSpec.ts +104 -0
- package/src/components/ApiExplorer/types.ts +84 -0
- package/src/components/AuthSimulationSelector.tsx +77 -0
- package/src/components/Branches/BranchesView.tsx +370 -0
- package/src/components/CronJobs/CronJobsView.tsx +346 -0
- package/src/components/JSEditor/JSEditor.tsx +1033 -0
- package/src/components/JSEditor/JSEditorSidebar.tsx +340 -0
- package/src/components/JSEditor/JSMonacoEditor.tsx +390 -0
- package/src/components/RLSEditor/PolicyEditor.tsx +444 -0
- package/src/components/RLSEditor/RLSEditor.tsx +692 -0
- package/src/components/RLSEditor/index.ts +1 -0
- package/src/components/RebaseStudio.tsx +121 -0
- package/src/components/SQLEditor/ExplainVisualizer.tsx +128 -0
- package/src/components/SQLEditor/MonacoEditor.tsx +203 -0
- package/src/components/SQLEditor/SQLEditor.tsx +1419 -0
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +174 -0
- package/src/components/SQLEditor/SchemaBrowser.tsx +158 -0
- package/src/components/SchemaVisualizer/RelationEdge.tsx +102 -0
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +665 -0
- package/src/components/SchemaVisualizer/TableNode.tsx +257 -0
- package/src/components/SchemaVisualizer/index.ts +5 -0
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +140 -0
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +397 -0
- package/src/components/StorageView/StorageView.tsx +1035 -0
- package/src/components/StudioHomePage.tsx +357 -0
- package/src/index.ts +31 -0
- package/src/utils/entities.ts +2 -0
- package/src/utils/pgColumnToProperty.test.ts +401 -0
- package/src/utils/pgColumnToProperty.ts +275 -0
- package/src/utils/sql_utils.test.ts +265 -0
- package/src/utils/sql_utils.ts +291 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Typography, cls, defaultBorderMixin, Button, Chip, CircularProgress, IconButton, Card, Paper , iconSize } from "@rebasepro/ui";
|
|
4
|
+
import { PauseIcon, CalendarIcon, RefreshCwIcon, PlayIcon, CheckCircleIcon, AlertCircleIcon, HistoryIcon } from "lucide-react";
|
|
5
|
+
import { useRebaseClient, useSnackbarController } from "@rebasepro/core";
|
|
6
|
+
import type { CronJobStatus, CronJobLogEntry } from "@rebasepro/types";
|
|
7
|
+
import type { RebaseClient } from "@rebasepro/client";
|
|
8
|
+
|
|
9
|
+
function formatDuration(ms: number): string {
|
|
10
|
+
if (ms < 1000) return `${ms}ms`;
|
|
11
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
12
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatRelative(iso: string | undefined): string {
|
|
16
|
+
if (!iso) return "—";
|
|
17
|
+
const d = new Date(iso);
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const diff = d.getTime() - now;
|
|
20
|
+
const abs = Math.abs(diff);
|
|
21
|
+
if (abs < 60000) return diff > 0 ? "in <1m" : "<1m ago";
|
|
22
|
+
if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }
|
|
23
|
+
if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }
|
|
24
|
+
return d.toLocaleString();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const stateColors: Record<string, string> = {
|
|
28
|
+
idle: "bg-emerald-500",
|
|
29
|
+
running: "bg-blue-500",
|
|
30
|
+
success: "bg-emerald-500",
|
|
31
|
+
error: "bg-red-500",
|
|
32
|
+
disabled: "bg-surface-400"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function CronJobsView() {
|
|
36
|
+
const client = useRebaseClient<RebaseClient>();
|
|
37
|
+
const snackbar = useSnackbarController();
|
|
38
|
+
const [jobs, setJobs] = useState<CronJobStatus[]>([]);
|
|
39
|
+
const [loading, setLoading] = useState(true);
|
|
40
|
+
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
41
|
+
const [logs, setLogs] = useState<CronJobLogEntry[]>([]);
|
|
42
|
+
const [logsLoading, setLogsLoading] = useState(false);
|
|
43
|
+
const [triggering, setTriggering] = useState<string | null>(null);
|
|
44
|
+
|
|
45
|
+
// Refs so effects never re-fire due to identity changes
|
|
46
|
+
const clientRef = useRef(client);
|
|
47
|
+
clientRef.current = client;
|
|
48
|
+
const snackbarRef = useRef(snackbar);
|
|
49
|
+
snackbarRef.current = snackbar;
|
|
50
|
+
|
|
51
|
+
// ── Fetch jobs on mount + poll every 15s ──
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
let cancelled = false;
|
|
54
|
+
|
|
55
|
+
async function load() {
|
|
56
|
+
const c = clientRef.current;
|
|
57
|
+
if (!c?.cron) {
|
|
58
|
+
setLoading(false);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const res = await c.cron.listJobs();
|
|
63
|
+
if (!cancelled) setJobs(res.jobs);
|
|
64
|
+
} catch (e: unknown) {
|
|
65
|
+
if (!cancelled) {
|
|
66
|
+
snackbarRef.current.open({
|
|
67
|
+
type: "error",
|
|
68
|
+
message: e instanceof Error ? e.message : String(e)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
if (!cancelled) setLoading(false);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
load();
|
|
77
|
+
const t = setInterval(load, 15_000);
|
|
78
|
+
return () => { cancelled = true; clearInterval(t); };
|
|
79
|
+
}, []); // runs once
|
|
80
|
+
|
|
81
|
+
// ── Fetch logs when selection changes ──
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!selectedId) {
|
|
84
|
+
setLogs([]);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let cancelled = false;
|
|
88
|
+
const c = clientRef.current;
|
|
89
|
+
if (!c?.cron) return;
|
|
90
|
+
|
|
91
|
+
setLogsLoading(true);
|
|
92
|
+
c.cron.getJobLogs(selectedId, { limit: 25 })
|
|
93
|
+
.then(res => { if (!cancelled) setLogs(res.logs); })
|
|
94
|
+
.catch(() => { if (!cancelled) setLogs([]); })
|
|
95
|
+
.finally(() => { if (!cancelled) setLogsLoading(false); });
|
|
96
|
+
|
|
97
|
+
return () => { cancelled = true; };
|
|
98
|
+
}, [selectedId]);
|
|
99
|
+
|
|
100
|
+
// ── Imperative helpers (not in any dep array) ──
|
|
101
|
+
async function refreshJobs() {
|
|
102
|
+
const c = clientRef.current;
|
|
103
|
+
if (!c?.cron) return;
|
|
104
|
+
try {
|
|
105
|
+
const res = await c.cron.listJobs();
|
|
106
|
+
setJobs(res.jobs);
|
|
107
|
+
} catch { /* swallow */ }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function refreshLogs(id: string) {
|
|
111
|
+
const c = clientRef.current;
|
|
112
|
+
if (!c?.cron) return;
|
|
113
|
+
setLogsLoading(true);
|
|
114
|
+
try {
|
|
115
|
+
const res = await c.cron.getJobLogs(id, { limit: 25 });
|
|
116
|
+
setLogs(res.logs);
|
|
117
|
+
} catch { setLogs([]); }
|
|
118
|
+
finally { setLogsLoading(false); }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const handleTrigger = async (id: string) => {
|
|
122
|
+
const c = clientRef.current;
|
|
123
|
+
if (!c?.cron) return;
|
|
124
|
+
setTriggering(id);
|
|
125
|
+
try {
|
|
126
|
+
await c.cron.triggerJob(id);
|
|
127
|
+
snackbarRef.current.open({ type: "success",
|
|
128
|
+
message: "Job triggered" });
|
|
129
|
+
await refreshJobs();
|
|
130
|
+
if (selectedId === id) refreshLogs(id);
|
|
131
|
+
} catch (e: unknown) {
|
|
132
|
+
snackbarRef.current.open({ type: "error",
|
|
133
|
+
message: e instanceof Error ? e.message : String(e) });
|
|
134
|
+
} finally { setTriggering(null); }
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleToggle = async (id: string, enabled: boolean) => {
|
|
138
|
+
const c = clientRef.current;
|
|
139
|
+
if (!c?.cron) return;
|
|
140
|
+
try {
|
|
141
|
+
await c.cron.toggleJob(id, enabled);
|
|
142
|
+
snackbarRef.current.open({ type: "success",
|
|
143
|
+
message: enabled ? "Job enabled" : "Job paused" });
|
|
144
|
+
await refreshJobs();
|
|
145
|
+
} catch (e: unknown) {
|
|
146
|
+
snackbarRef.current.open({ type: "error",
|
|
147
|
+
message: e instanceof Error ? e.message : String(e) });
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const selectedJob = jobs.find(j => j.id === selectedId);
|
|
152
|
+
|
|
153
|
+
if (loading) return <div className="flex items-center justify-center h-full"><CircularProgress/></div>;
|
|
154
|
+
|
|
155
|
+
if (jobs.length === 0) return (
|
|
156
|
+
<div className="flex flex-col items-center justify-center h-full gap-4 text-center p-8">
|
|
157
|
+
<CalendarIcon size={iconSize.medium} className="text-surface-300 dark:text-surface-600"/>
|
|
158
|
+
<Typography variant="h6" color="secondary">No Cron Jobs Registered</Typography>
|
|
159
|
+
<Typography variant="body2" color="disabled" className="max-w-md">
|
|
160
|
+
Create a file in your <code className="text-xs bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded font-mono">crons/</code> directory that default-exports a <code className="text-xs bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded font-mono">CronJobDefinition</code>.
|
|
161
|
+
</Typography>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className="flex h-full w-full overflow-hidden bg-white dark:bg-surface-950">
|
|
167
|
+
{/* ── Job List ── */}
|
|
168
|
+
<div className={cls("flex flex-col w-[340px] min-w-[280px] border-r h-full", defaultBorderMixin)}>
|
|
169
|
+
<div className={cls("flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
|
|
170
|
+
<div className="flex items-center gap-2">
|
|
171
|
+
<CalendarIcon size={iconSize.smallest} className="text-primary"/>
|
|
172
|
+
<Typography variant="subtitle2" className="font-semibold">Cron Jobs</Typography>
|
|
173
|
+
<Chip size="smallest" className="bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300">{jobs.length}</Chip>
|
|
174
|
+
</div>
|
|
175
|
+
<IconButton size="small" onClick={refreshJobs} title="Refresh"><RefreshCwIcon size={iconSize.smallest}/></IconButton>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="flex-1 overflow-y-auto p-2 space-y-1">
|
|
178
|
+
{jobs.map(job => (
|
|
179
|
+
<div
|
|
180
|
+
key={job.id}
|
|
181
|
+
onClick={() => setSelectedId(job.id)}
|
|
182
|
+
className={cls(
|
|
183
|
+
"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all",
|
|
184
|
+
selectedId === job.id
|
|
185
|
+
? "bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30"
|
|
186
|
+
: "hover:bg-surface-100 dark:hover:bg-surface-950"
|
|
187
|
+
)}
|
|
188
|
+
>
|
|
189
|
+
<div className={cls("w-2 h-2 rounded-full shrink-0", stateColors[job.state] || "bg-surface-400")}/>
|
|
190
|
+
<div className="flex-1 min-w-0">
|
|
191
|
+
<Typography variant="body2" className="truncate font-medium text-[13px]">{job.name}</Typography>
|
|
192
|
+
<Typography variant="caption" color="secondary" className="truncate text-[11px] font-mono">{job.schedule}</Typography>
|
|
193
|
+
</div>
|
|
194
|
+
{job.state === "running" && <CircularProgress size="smallest"/>}
|
|
195
|
+
</div>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{/* ── Detail Panel ── */}
|
|
201
|
+
<div className="flex-1 flex flex-col min-w-0 h-full overflow-hidden">
|
|
202
|
+
{!selectedJob ? (
|
|
203
|
+
<div className="flex items-center justify-center h-full">
|
|
204
|
+
<Typography variant="body2" color="disabled">Select a cron job to view details</Typography>
|
|
205
|
+
</div>
|
|
206
|
+
) : (
|
|
207
|
+
<>
|
|
208
|
+
{/* Header */}
|
|
209
|
+
<div className={cls("flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]", defaultBorderMixin)}>
|
|
210
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
211
|
+
<div className={cls("w-2.5 h-2.5 rounded-full", stateColors[selectedJob.state])}/>
|
|
212
|
+
<div className="min-w-0">
|
|
213
|
+
<Typography variant="subtitle1" className="font-semibold truncate">{selectedJob.name}</Typography>
|
|
214
|
+
{selectedJob.description && <Typography variant="caption" color="secondary" className="truncate">{selectedJob.description}</Typography>}
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
218
|
+
<IconButton title={selectedJob.enabled ? "Pause job" : "Enable job"} size="small" onClick={() => handleToggle(selectedJob.id, !selectedJob.enabled)}>
|
|
219
|
+
{selectedJob.enabled ? <PauseIcon size={iconSize.small}/> : <PlayIcon size={iconSize.smallest}/>}
|
|
220
|
+
</IconButton>
|
|
221
|
+
<Button
|
|
222
|
+
size="small"
|
|
223
|
+
color="primary"
|
|
224
|
+
onClick={() => handleTrigger(selectedJob.id)}
|
|
225
|
+
disabled={triggering === selectedJob.id}
|
|
226
|
+
startIcon={triggering === selectedJob.id ? <CircularProgress size="smallest"/> : <PlayIcon size={iconSize.smallest}/>}
|
|
227
|
+
>
|
|
228
|
+
Run Now
|
|
229
|
+
</Button>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
{/* Stats Cards */}
|
|
234
|
+
<div className="px-5 py-4 bg-surface-50 dark:bg-surface-900/50">
|
|
235
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
236
|
+
<StatCard label="Schedule" value={selectedJob.schedule} mono/>
|
|
237
|
+
<StatCard label="Last Run" value={formatRelative(selectedJob.lastRunAt)}/>
|
|
238
|
+
<StatCard label="Next Run" value={selectedJob.enabled ? formatRelative(selectedJob.nextRunAt) : "Paused"}/>
|
|
239
|
+
<StatCard label="Duration" value={selectedJob.lastDurationMs !== undefined ? formatDuration(selectedJob.lastDurationMs) : "—"}/>
|
|
240
|
+
</div>
|
|
241
|
+
<div className="grid grid-cols-3 gap-3 mt-3">
|
|
242
|
+
<StatCard label="Status" value={selectedJob.state.toUpperCase()} chipColor={selectedJob.state === "error" ? "red" : selectedJob.state === "disabled" ? "gray" : "green"}/>
|
|
243
|
+
<StatCard label="Total Runs" value={String(selectedJob.totalRuns)}/>
|
|
244
|
+
<StatCard label="Failures" value={String(selectedJob.totalFailures)} highlight={selectedJob.totalFailures > 0}/>
|
|
245
|
+
</div>
|
|
246
|
+
{selectedJob.lastError && (
|
|
247
|
+
<div className="mt-3 p-3 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50">
|
|
248
|
+
<div className="flex items-center gap-2 mb-1">
|
|
249
|
+
<AlertCircleIcon size={iconSize.smallest} className="text-red-500"/>
|
|
250
|
+
<Typography variant="caption" className="font-semibold text-red-700 dark:text-red-400">Last Error</Typography>
|
|
251
|
+
</div>
|
|
252
|
+
<Typography variant="caption" className="font-mono text-red-600 dark:text-red-300 text-[11px] break-all">{selectedJob.lastError}</Typography>
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
{/* Logs Section */}
|
|
258
|
+
<div className={cls("flex items-center justify-between px-5 py-2 border-y bg-white dark:bg-surface-950", defaultBorderMixin)}>
|
|
259
|
+
<div className="flex items-center gap-2">
|
|
260
|
+
<HistoryIcon size={iconSize.smallest} className="text-surface-400"/>
|
|
261
|
+
<Typography variant="subtitle2" className="font-semibold text-[13px]">Execution History</Typography>
|
|
262
|
+
</div>
|
|
263
|
+
<IconButton size="small" onClick={() => refreshLogs(selectedJob.id)} title="Refresh logs"><RefreshCwIcon size={iconSize.smallest}/></IconButton>
|
|
264
|
+
</div>
|
|
265
|
+
<div className="flex-1 overflow-y-auto">
|
|
266
|
+
{logsLoading ? (
|
|
267
|
+
<div className="flex justify-center p-8"><CircularProgress size="small"/></div>
|
|
268
|
+
) : logs.length === 0 ? (
|
|
269
|
+
<div className="flex items-center justify-center h-32">
|
|
270
|
+
<Typography variant="body2" color="disabled">No executions yet</Typography>
|
|
271
|
+
</div>
|
|
272
|
+
) : (
|
|
273
|
+
<div className="divide-y divide-surface-100 dark:divide-surface-950">
|
|
274
|
+
{logs.map((log, idx) => (
|
|
275
|
+
<LogRow key={idx} log={log}/>
|
|
276
|
+
))}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
</div>
|
|
280
|
+
</>
|
|
281
|
+
)}
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function StatCard({ label, value, mono, chipColor, highlight }: {
|
|
288
|
+
label: string; value: string; mono?: boolean; chipColor?: string; highlight?: boolean;
|
|
289
|
+
}) {
|
|
290
|
+
return (
|
|
291
|
+
<div className={cls("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", defaultBorderMixin)}>
|
|
292
|
+
<Typography variant="caption" color="secondary" className="text-[10px] uppercase tracking-wider font-medium">{label}</Typography>
|
|
293
|
+
<Typography variant="body2" className={cls(
|
|
294
|
+
"mt-0.5 font-semibold text-[13px]",
|
|
295
|
+
mono && "font-mono",
|
|
296
|
+
highlight && "text-red-500 dark:text-red-400",
|
|
297
|
+
chipColor === "red" && "text-red-500",
|
|
298
|
+
chipColor === "green" && "text-emerald-500",
|
|
299
|
+
chipColor === "gray" && "text-surface-400"
|
|
300
|
+
)}>{value}</Typography>
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function LogRow({ log }: { log: CronJobLogEntry }) {
|
|
306
|
+
const [expanded, setExpanded] = useState(false);
|
|
307
|
+
return (
|
|
308
|
+
<div className="px-5 py-2.5 hover:bg-surface-50 dark:hover:bg-surface-950/50 transition-colors">
|
|
309
|
+
<div className="flex items-center gap-3 cursor-pointer" onClick={() => setExpanded(!expanded)}>
|
|
310
|
+
{log.success
|
|
311
|
+
? <CheckCircleIcon size={iconSize.smallest} className="text-emerald-500 shrink-0"/>
|
|
312
|
+
: <AlertCircleIcon size={iconSize.smallest} className="text-red-500 shrink-0"/>}
|
|
313
|
+
<div className="flex-1 min-w-0">
|
|
314
|
+
<Typography variant="caption" className="font-mono text-[11px] text-surface-500">{new Date(log.startedAt).toLocaleString()}</Typography>
|
|
315
|
+
</div>
|
|
316
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
317
|
+
{log.manual && <Chip size="smallest" className="bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300">manual</Chip>}
|
|
318
|
+
<Typography variant="caption" className="font-mono text-[11px]">{formatDuration(log.durationMs)}</Typography>
|
|
319
|
+
<svg className={cls("w-3 h-3 transition-transform text-surface-400", expanded && "rotate-180")} fill="currentColor" viewBox="0 0 20 20"><path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"/></svg>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
{expanded && (
|
|
323
|
+
<div className="mt-2 ml-6 space-y-2">
|
|
324
|
+
{log.error && (
|
|
325
|
+
<div className="p-2 rounded bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50">
|
|
326
|
+
<Typography variant="caption" className="font-mono text-[11px] text-red-600 dark:text-red-300 break-all">{log.error}</Typography>
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
329
|
+
{log.logs.length > 0 && (
|
|
330
|
+
<div className="p-2 rounded bg-surface-50 dark:bg-surface-900 border border-surface-200 dark:border-surface-700 max-h-40 overflow-auto">
|
|
331
|
+
{log.logs.map((line, i) => (
|
|
332
|
+
<div key={i} className="font-mono text-[11px] text-surface-600 dark:text-surface-400 leading-relaxed">{line}</div>
|
|
333
|
+
))}
|
|
334
|
+
</div>
|
|
335
|
+
)}
|
|
336
|
+
{log.result !== undefined && (
|
|
337
|
+
<div className="p-2 rounded bg-surface-50 dark:bg-surface-900 border border-surface-200 dark:border-surface-700">
|
|
338
|
+
<Typography variant="caption" className="text-[10px] uppercase tracking-wider text-surface-400 mb-1 block">Result</Typography>
|
|
339
|
+
<pre className="font-mono text-[11px] text-surface-600 dark:text-surface-400 whitespace-pre-wrap break-all">{JSON.stringify(log.result, null, 2)}</pre>
|
|
340
|
+
</div>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
)}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|