@oneuptime/common 11.7.2 → 11.7.4
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/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
- package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Index.ts +5 -0
- package/Models/DatabaseModels/LogDropFilter.ts +89 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
- package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
- package/Models/DatabaseModels/TableView.ts +40 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
- package/Server/API/AlertAPI.ts +32 -17
- package/Server/API/BaseAPI.ts +31 -2
- package/Server/API/BaseAnalyticsAPI.ts +36 -2
- package/Server/API/CommonAPI.ts +57 -0
- package/Server/API/IncidentAPI.ts +60 -33
- package/Server/API/IncidentEpisodeAPI.ts +31 -16
- package/Server/API/MonitorAPI.ts +30 -0
- package/Server/API/MonitorTemplateAPI.ts +8 -0
- package/Server/API/ProbeAPI.ts +196 -15
- package/Server/API/ProjectAPI.ts +79 -1
- package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
- package/Server/API/StatusPageAPI.ts +74 -37
- package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AlertEpisodeService.ts +47 -0
- package/Server/Services/AlertService.ts +125 -0
- package/Server/Services/BillingService.ts +98 -0
- package/Server/Services/DatabaseService.ts +184 -20
- package/Server/Services/IncidentEpisodeService.ts +47 -0
- package/Server/Services/IncidentService.ts +123 -0
- package/Server/Services/IncidentTemplateService.ts +123 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LogDropFilterService.ts +133 -0
- package/Server/Services/MonitorProbeService.ts +94 -4
- package/Server/Services/MonitorService.ts +48 -43
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
- package/Server/Services/ProbeService.ts +66 -0
- package/Server/Services/ProjectService.ts +131 -7
- package/Server/Services/RecommendationDismissalService.ts +52 -0
- package/Server/Services/RunbookAgentJobService.ts +68 -7
- package/Server/Services/ScheduledMaintenanceService.ts +103 -0
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
- package/Server/Services/StatusPageGroupService.ts +186 -0
- package/Server/Services/TraceDropFilterService.ts +133 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
- package/Server/Types/Workflow/Components/API/Get.ts +12 -0
- package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
- package/Server/Types/Workflow/Components/API/Post.ts +12 -0
- package/Server/Types/Workflow/Components/API/Put.ts +13 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
- package/Server/Utils/DropFilterValidation.ts +127 -0
- package/Server/Utils/Errors.ts +5 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
- package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
- package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
- package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
- package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
- package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
- package/Tests/Server/API/ProbeAPI.test.ts +422 -53
- package/Tests/Server/API/ProjectAPI.test.ts +180 -0
- package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
- package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
- package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
- package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
- package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
- package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
- package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
- package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
- package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
- package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
- package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
- package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
- package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
- package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
- package/Tests/Types/DateUserTimezone.test.ts +52 -0
- package/Tests/Types/JSONFunctions.test.ts +103 -1
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
- package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
- package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
- package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
- package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
- package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
- package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
- package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
- package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
- package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
- package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
- package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
- package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
- package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
- package/Tests/UI/Components/InfoCard.test.tsx +358 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
- package/Tests/UI/Components/Modal.test.tsx +43 -5
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +999 -0
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
- package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
- package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
- package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
- package/Tests/Utils/UserPreferences.test.ts +563 -0
- package/Types/Date.ts +15 -6
- package/Types/JSONFunctions.ts +7 -2
- package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
- package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
- package/Types/Permission.ts +45 -0
- package/Types/Recommendation/RecommendationType.ts +20 -0
- package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
- package/Types/Runbook/RunbookStepTimeout.ts +202 -0
- package/Types/Telemetry/DropFilterSampling.ts +92 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
- package/UI/Components/Accordion/Accordion.tsx +10 -2
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
- package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
- package/UI/Components/Error/PageError.tsx +0 -4
- package/UI/Components/ErrorBoundary.tsx +252 -15
- package/UI/Components/InfoCard/InfoCard.tsx +58 -1
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
- package/UI/Components/MasterPage/MasterPage.tsx +65 -0
- package/UI/Components/Modal/Modal.tsx +16 -60
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
- package/UI/Components/ModelTable/BaseModelTable.tsx +503 -71
- package/UI/Components/ModelTable/Column.ts +17 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +523 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
- package/UI/Components/ModelTable/TableView.tsx +24 -2
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
- package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
- package/UI/Components/SideMenu/SideMenu.tsx +24 -4
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
- package/UI/Components/Table/Table.tsx +14 -1
- package/UI/Components/Table/TableRow.tsx +180 -175
- package/UI/Components/Table/Types/Column.ts +2 -0
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
- package/UI/Utils/DropdownAlignment.ts +162 -0
- package/UI/Utils/TableFilterUrlState.ts +59 -20
- package/Utils/API.ts +106 -2
- package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
- package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
- package/Utils/StatusPage/GroupTree.ts +307 -0
- package/Utils/StatusPage/ResourceUptime.ts +90 -17
- package/Utils/UserPreferences.ts +53 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +40 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +22 -13
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +22 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +42 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +43 -26
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +23 -0
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
- package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +131 -15
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +62 -3
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +61 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +95 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +71 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +140 -17
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +100 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogDropFilterService.js +93 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +73 -5
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +34 -39
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +64 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +103 -7
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
- package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
- package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
- package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
- package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
- package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
- package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
- package/build/dist/Server/Utils/Errors.js +3 -0
- package/build/dist/Server/Utils/Errors.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Types/Date.js +14 -4
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +8 -3
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
- package/build/dist/Types/Permission.js +40 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
- package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Error/PageError.js +1 -3
- package/build/dist/UI/Components/Error/PageError.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +145 -11
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +4 -32
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +308 -40
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +230 -0
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
- package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +15 -2
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +11 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
- package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
- package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
- package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
- package/build/dist/Utils/API.js +39 -2
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/UserPreferences.js +33 -0
- package/build/dist/Utils/UserPreferences.js.map +1 -1
- package/package.json +1 -1
|
@@ -58,6 +58,23 @@ import TableColumn from "../Table/Types/Column";
|
|
|
58
58
|
import FieldType from "../Types/FieldType";
|
|
59
59
|
import ModelTableColumn from "./Column";
|
|
60
60
|
import Columns from "./Columns";
|
|
61
|
+
import ColumnCustomizationModal from "./ColumnCustomizationModal";
|
|
62
|
+
import {
|
|
63
|
+
ColumnPreference,
|
|
64
|
+
CustomizableColumn,
|
|
65
|
+
applyColumnPreference,
|
|
66
|
+
buildColumnPreference,
|
|
67
|
+
fromJSON as columnPreferenceFromJSON,
|
|
68
|
+
getCustomizableColumns,
|
|
69
|
+
isEmptyColumnPreference,
|
|
70
|
+
sanitizeColumnPreference,
|
|
71
|
+
getColumnIds,
|
|
72
|
+
toJSON as columnPreferenceToJSON,
|
|
73
|
+
} from "./ColumnPreference";
|
|
74
|
+
import { CustomFieldsColumnKey } from "./CustomFieldColumns";
|
|
75
|
+
import useCustomFieldColumns, {
|
|
76
|
+
CustomFieldColumnsResult,
|
|
77
|
+
} from "./useCustomFieldColumns";
|
|
61
78
|
import { getExportKeysFromColumn } from "./ExportFromColumns";
|
|
62
79
|
import {
|
|
63
80
|
getRelationSelectFromColumns,
|
|
@@ -323,6 +340,26 @@ export interface BaseTableProps<
|
|
|
323
340
|
* outlive the page.
|
|
324
341
|
*/
|
|
325
342
|
disableUrlState?: boolean | undefined;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Hide the "Columns" control and always render the declared column set.
|
|
346
|
+
*
|
|
347
|
+
* Column customization is on by default for every table that renders
|
|
348
|
+
* columns. Turn it off for tables whose layout is load-bearing — a
|
|
349
|
+
* two-column key/value table, or one the surrounding page reads positions
|
|
350
|
+
* out of.
|
|
351
|
+
*/
|
|
352
|
+
disableColumnCustomization?: boolean | undefined;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* The model holding this resource's custom field *definitions* — e.g.
|
|
356
|
+
* `MonitorCustomField` for a table of monitors. Set it and every custom
|
|
357
|
+
* field the project has defined becomes an optional column, off by default
|
|
358
|
+
* and listed in the column picker.
|
|
359
|
+
*
|
|
360
|
+
* Only meaningful for resources with a `customFields` column of their own.
|
|
361
|
+
*/
|
|
362
|
+
customFieldsModelType?: { new (): BaseModel } | undefined;
|
|
326
363
|
}
|
|
327
364
|
|
|
328
365
|
export interface ComponentProps<
|
|
@@ -395,6 +432,115 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
395
432
|
[],
|
|
396
433
|
);
|
|
397
434
|
|
|
435
|
+
/*
|
|
436
|
+
* ---------------------------------------------------------------------
|
|
437
|
+
* Viewer-customizable columns
|
|
438
|
+
* ---------------------------------------------------------------------
|
|
439
|
+
*
|
|
440
|
+
* The viewer's layout (which columns are on, and in what order) is a
|
|
441
|
+
* personal, presentational preference, so it lives in localStorage next to
|
|
442
|
+
* the page size rather than on the server: saved views are named, explicit,
|
|
443
|
+
* plan-gated and need write permission, none of which should stand between
|
|
444
|
+
* someone and hiding a column they don't care about. A saved view can still
|
|
445
|
+
* carry a layout of its own, and when one is active it wins.
|
|
446
|
+
*
|
|
447
|
+
* Custom fields are appended to the declared columns as additional,
|
|
448
|
+
* off-by-default columns; from here on they are ordinary columns.
|
|
449
|
+
*/
|
|
450
|
+
const customFieldColumns: CustomFieldColumnsResult<TBaseModel> =
|
|
451
|
+
useCustomFieldColumns<TBaseModel>({
|
|
452
|
+
customFieldsModelType: props.customFieldsModelType,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const isColumnCustomizationEnabled: boolean = Boolean(
|
|
456
|
+
!props.disableColumnCustomization &&
|
|
457
|
+
props.userPreferencesKey &&
|
|
458
|
+
showAs !== ShowAs.OrderedStatesList,
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
const allColumns: Columns<TBaseModel> = useMemo(() => {
|
|
462
|
+
if (customFieldColumns.columns.length === 0) {
|
|
463
|
+
return props.columns || [];
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return [...(props.columns || []), ...customFieldColumns.columns];
|
|
467
|
+
}, [props.columns, customFieldColumns.columns]);
|
|
468
|
+
|
|
469
|
+
type ReadStoredColumnPreferenceFunction = () => ColumnPreference | null;
|
|
470
|
+
|
|
471
|
+
const readStoredColumnPreference: ReadStoredColumnPreferenceFunction =
|
|
472
|
+
(): ColumnPreference | null => {
|
|
473
|
+
if (!props.userPreferencesKey) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return columnPreferenceFromJSON(
|
|
478
|
+
UserPreferences.getUserPreferenceByTypeAsJSON({
|
|
479
|
+
key: props.userPreferencesKey,
|
|
480
|
+
userPreferenceType: UserPreferenceType.BaseModelTableColumns,
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const [columnPreference, setColumnPreference] =
|
|
486
|
+
useState<ColumnPreference | null>(readStoredColumnPreference);
|
|
487
|
+
|
|
488
|
+
const [showColumnCustomizationModal, setShowColumnCustomizationModal] =
|
|
489
|
+
useState<boolean>(false);
|
|
490
|
+
|
|
491
|
+
/*
|
|
492
|
+
* Ids are derived over the *whole* declared set, so a column dropping out
|
|
493
|
+
* for lack of permission can never renumber the ones that remain.
|
|
494
|
+
*/
|
|
495
|
+
const allColumnIds: Array<string> = useMemo(() => {
|
|
496
|
+
return getColumnIds<TBaseModel>(allColumns);
|
|
497
|
+
}, [allColumns]);
|
|
498
|
+
|
|
499
|
+
const effectiveColumnPreference: ColumnPreference | null = useMemo(() => {
|
|
500
|
+
if (!isColumnCustomizationEnabled) {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/*
|
|
505
|
+
* A stored layout outlives the release that wrote it, so anything naming
|
|
506
|
+
* a column this table no longer has is dropped before it is applied.
|
|
507
|
+
*/
|
|
508
|
+
return sanitizeColumnPreference({
|
|
509
|
+
preference: columnPreference,
|
|
510
|
+
knownColumnIds: allColumnIds,
|
|
511
|
+
});
|
|
512
|
+
}, [columnPreference, allColumnIds, isColumnCustomizationEnabled]);
|
|
513
|
+
|
|
514
|
+
type SaveColumnPreferenceFunction = (
|
|
515
|
+
preference: ColumnPreference | null,
|
|
516
|
+
) => void;
|
|
517
|
+
|
|
518
|
+
const saveColumnPreference: SaveColumnPreferenceFunction = (
|
|
519
|
+
preference: ColumnPreference | null,
|
|
520
|
+
): void => {
|
|
521
|
+
setColumnPreference(preference);
|
|
522
|
+
|
|
523
|
+
if (!props.userPreferencesKey) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const json: JSONObject | null = columnPreferenceToJSON(preference);
|
|
528
|
+
|
|
529
|
+
if (!json) {
|
|
530
|
+
UserPreferences.removeUserPreferenceByType({
|
|
531
|
+
key: props.userPreferencesKey,
|
|
532
|
+
userPreferenceType: UserPreferenceType.BaseModelTableColumns,
|
|
533
|
+
});
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
UserPreferences.saveUserPreferenceByTypeAsJSON({
|
|
538
|
+
key: props.userPreferencesKey,
|
|
539
|
+
userPreferenceType: UserPreferenceType.BaseModelTableColumns,
|
|
540
|
+
value: json,
|
|
541
|
+
});
|
|
542
|
+
};
|
|
543
|
+
|
|
398
544
|
const [classicTableFilters, setClassicTableFilters] = useState<
|
|
399
545
|
Array<ClassicFilterType<TBaseModel>>
|
|
400
546
|
>([]);
|
|
@@ -519,6 +665,66 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
519
665
|
const [error, setError] = useState<string>("");
|
|
520
666
|
const [tableFilterError, setTableFilterError] = useState<string>("");
|
|
521
667
|
|
|
668
|
+
/*
|
|
669
|
+
* Auto-detect label support from the existing filters array. We look for
|
|
670
|
+
* the filter whose `filterEntityType` class name is "Label" and reuse its
|
|
671
|
+
* dropdown wiring (entity type, fetch query, label/value field names) so
|
|
672
|
+
* search inherits whatever scoping the filter popup already had.
|
|
673
|
+
*
|
|
674
|
+
* Computed before the search state below because the `labels` slice of a
|
|
675
|
+
* restored URL is only meaningful when this is non-null.
|
|
676
|
+
*/
|
|
677
|
+
type LabelFilterConfig = {
|
|
678
|
+
fieldKey: string;
|
|
679
|
+
entityType: any;
|
|
680
|
+
fetchQuery: any;
|
|
681
|
+
labelField: string;
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
const labelFilterConfig: LabelFilterConfig | null = useMemo(() => {
|
|
685
|
+
const filter: Filter<TBaseModel> | undefined = props.filters.find(
|
|
686
|
+
(f: Filter<TBaseModel>) => {
|
|
687
|
+
return (
|
|
688
|
+
f.filterEntityType &&
|
|
689
|
+
(f.filterEntityType as any).name === "Label" &&
|
|
690
|
+
f.field &&
|
|
691
|
+
f.filterDropdownField
|
|
692
|
+
);
|
|
693
|
+
},
|
|
694
|
+
);
|
|
695
|
+
if (!filter || !filter.field || !filter.filterDropdownField) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
const fieldKey: string | undefined = Object.keys(filter.field)[0];
|
|
699
|
+
if (!fieldKey) {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
fieldKey,
|
|
704
|
+
entityType: filter.filterEntityType,
|
|
705
|
+
fetchQuery: filter.filterQuery || {},
|
|
706
|
+
labelField: filter.filterDropdownField.label,
|
|
707
|
+
};
|
|
708
|
+
}, [props.filters]);
|
|
709
|
+
|
|
710
|
+
/*
|
|
711
|
+
* The label chips are a feature of the in-search Label filter, so a table
|
|
712
|
+
* without one has to ignore the URL's `labels` slice entirely.
|
|
713
|
+
*
|
|
714
|
+
* Tables that have since moved Labels out to a facet chip (Monitors,
|
|
715
|
+
* Network Devices) still receive `labels` from older shared/bookmarked
|
|
716
|
+
* links. Seeding those ids anyway produced a chip the table could neither
|
|
717
|
+
* name (`availableLabels` is only fetched when there *is* a config) nor
|
|
718
|
+
* apply (`buildSearchQueryFragment` skips the constraint for the same
|
|
719
|
+
* reason): a force-expanded search box with a raw-UUID pill sitting above a
|
|
720
|
+
* completely unfiltered list, re-persisted on every write until the user
|
|
721
|
+
* clicked its ×. Dropping it here also means the next view write omits the
|
|
722
|
+
* key, so the URL heals itself.
|
|
723
|
+
*/
|
|
724
|
+
const restoredLabelIds: Array<string> = labelFilterConfig
|
|
725
|
+
? initialUrlState.view.labels || []
|
|
726
|
+
: [];
|
|
727
|
+
|
|
522
728
|
const [searchText, setSearchText] = useState<string>(
|
|
523
729
|
initialUrlState.view.search || "",
|
|
524
730
|
);
|
|
@@ -527,9 +733,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
527
733
|
);
|
|
528
734
|
const [isSearchFocused, setIsSearchFocused] = useState<boolean>(false);
|
|
529
735
|
const [isSearchExpanded, setIsSearchExpanded] = useState<boolean>(
|
|
530
|
-
Boolean(
|
|
531
|
-
initialUrlState.view.search || (initialUrlState.view.labels || []).length,
|
|
532
|
-
),
|
|
736
|
+
Boolean(initialUrlState.view.search || restoredLabelIds.length),
|
|
533
737
|
);
|
|
534
738
|
const searchInputRef: React.RefObject<HTMLInputElement> =
|
|
535
739
|
React.useRef<HTMLInputElement>(null!);
|
|
@@ -551,7 +755,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
551
755
|
const [selectedLabels, setSelectedLabels] = useState<
|
|
552
756
|
Array<SearchLabelOption>
|
|
553
757
|
>(() => {
|
|
554
|
-
return
|
|
758
|
+
return restoredLabelIds.map((id: string) => {
|
|
555
759
|
return { id: id, name: id, color: "#94a3b8" };
|
|
556
760
|
});
|
|
557
761
|
});
|
|
@@ -647,43 +851,24 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
647
851
|
}, [debouncedSearchText, selectedLabelIdsKey]);
|
|
648
852
|
|
|
649
853
|
/*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
*
|
|
854
|
+
* `props.filters` is not frozen — a page can rebuild it after mount, and
|
|
855
|
+
* that is exactly how a Labels filter leaves the popup for the facet bar.
|
|
856
|
+
* Chips left over from a config that no longer exists are unnameable and
|
|
857
|
+
* unappliable, so drop them here too rather than only at mount.
|
|
654
858
|
*/
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
fetchQuery: any;
|
|
659
|
-
labelField: string;
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
const labelFilterConfig: LabelFilterConfig | null = useMemo(() => {
|
|
663
|
-
const filter: Filter<TBaseModel> | undefined = props.filters.find(
|
|
664
|
-
(f: Filter<TBaseModel>) => {
|
|
665
|
-
return (
|
|
666
|
-
f.filterEntityType &&
|
|
667
|
-
(f.filterEntityType as any).name === "Label" &&
|
|
668
|
-
f.field &&
|
|
669
|
-
f.filterDropdownField
|
|
670
|
-
);
|
|
671
|
-
},
|
|
672
|
-
);
|
|
673
|
-
if (!filter || !filter.field || !filter.filterDropdownField) {
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
676
|
-
const fieldKey: string | undefined = Object.keys(filter.field)[0];
|
|
677
|
-
if (!fieldKey) {
|
|
678
|
-
return null;
|
|
859
|
+
useEffect(() => {
|
|
860
|
+
if (labelFilterConfig) {
|
|
861
|
+
return;
|
|
679
862
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
863
|
+
|
|
864
|
+
setSelectedLabels((existing: Array<SearchLabelOption>) => {
|
|
865
|
+
/*
|
|
866
|
+
* Same array when there is nothing to clear, so this costs an idle table
|
|
867
|
+
* no re-render.
|
|
868
|
+
*/
|
|
869
|
+
return existing.length === 0 ? existing : [];
|
|
870
|
+
});
|
|
871
|
+
}, [labelFilterConfig]);
|
|
687
872
|
|
|
688
873
|
// Fetch labels on first search expansion if this resource supports them.
|
|
689
874
|
useEffect(() => {
|
|
@@ -903,8 +1088,11 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
903
1088
|
|
|
904
1089
|
const getRelationSelect: GetRelationSelectFunction =
|
|
905
1090
|
(): Select<TBaseModel> => {
|
|
1091
|
+
/*
|
|
1092
|
+
* Deliberately the *unfiltered* column set. See getSelect() below.
|
|
1093
|
+
*/
|
|
906
1094
|
return getRelationSelectFromColumns<TBaseModel>({
|
|
907
|
-
columns:
|
|
1095
|
+
columns: allColumns,
|
|
908
1096
|
model: model,
|
|
909
1097
|
});
|
|
910
1098
|
};
|
|
@@ -950,7 +1138,30 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
950
1138
|
const accessControl: Dictionary<ColumnAccessControl> =
|
|
951
1139
|
model.getColumnAccessControlForAllColumns();
|
|
952
1140
|
|
|
953
|
-
|
|
1141
|
+
/*
|
|
1142
|
+
* The viewer's layout is applied to the *input* of this loop, never to
|
|
1143
|
+
* the finished array: the Actions column is appended below and has to
|
|
1144
|
+
* stay last, and it is generated rather than declared, so it is not the
|
|
1145
|
+
* viewer's to move or switch off.
|
|
1146
|
+
*/
|
|
1147
|
+
const columnsToRender: Columns<TBaseModel> =
|
|
1148
|
+
applyColumnPreference<TBaseModel>({
|
|
1149
|
+
columns: allColumns,
|
|
1150
|
+
preference: effectiveColumnPreference,
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
const columnIdByColumn: Map<
|
|
1154
|
+
ModelTableColumn<TBaseModel>,
|
|
1155
|
+
string
|
|
1156
|
+
> = new Map();
|
|
1157
|
+
|
|
1158
|
+
allColumns.forEach(
|
|
1159
|
+
(column: ModelTableColumn<TBaseModel>, index: number) => {
|
|
1160
|
+
columnIdByColumn.set(column, allColumnIds[index] as string);
|
|
1161
|
+
},
|
|
1162
|
+
);
|
|
1163
|
+
|
|
1164
|
+
for (const column of columnsToRender) {
|
|
954
1165
|
const hasPermission: boolean =
|
|
955
1166
|
hasPermissionToReadColumn(column) || User.isMasterAdmin();
|
|
956
1167
|
const key: keyof TBaseModel | null = getColumnKey(column);
|
|
@@ -983,6 +1194,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
983
1194
|
|
|
984
1195
|
columns.push({
|
|
985
1196
|
...column,
|
|
1197
|
+
id: columnIdByColumn.get(column),
|
|
986
1198
|
disableSort: column.disableSort || shouldDisableSort(key),
|
|
987
1199
|
key: columnKey,
|
|
988
1200
|
/*
|
|
@@ -1692,14 +1904,39 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1692
1904
|
type GetSelectFunction = () => Select<TBaseModel>;
|
|
1693
1905
|
|
|
1694
1906
|
const getSelect: GetSelectFunction = (): Select<TBaseModel> => {
|
|
1907
|
+
/*
|
|
1908
|
+
* Every declared column, including the ones the viewer has switched off.
|
|
1909
|
+
*
|
|
1910
|
+
* Hiding must not narrow the request. A column's `getElement` is arbitrary
|
|
1911
|
+
* caller code that can read any field on the row — a "Status" cell that
|
|
1912
|
+
* also reads `currentMonitorStatus`, say — so dropping a hidden column's
|
|
1913
|
+
* fields from the select would silently blank a *different*, visible cell,
|
|
1914
|
+
* and there is no way to detect that statically. The sort field is
|
|
1915
|
+
* likewise independent of what is on screen. The cost is a few unused
|
|
1916
|
+
* fields on the wire.
|
|
1917
|
+
*/
|
|
1695
1918
|
const selectFields: Select<TBaseModel> = getSelectFromColumns<TBaseModel>({
|
|
1696
|
-
columns:
|
|
1919
|
+
columns: allColumns,
|
|
1697
1920
|
model: model,
|
|
1698
1921
|
hasPermissionToReadField: (field: string): boolean => {
|
|
1699
1922
|
return hasPermissionToReadField(field as keyof TBaseModel);
|
|
1700
1923
|
},
|
|
1701
1924
|
});
|
|
1702
1925
|
|
|
1926
|
+
/*
|
|
1927
|
+
* Custom field columns arrive asynchronously, but the table does not
|
|
1928
|
+
* refetch when its column set changes — so the JSON column that backs
|
|
1929
|
+
* every one of them is selected up front, off the synchronous prop, and
|
|
1930
|
+
* the values are there the moment the definitions land.
|
|
1931
|
+
*/
|
|
1932
|
+
if (
|
|
1933
|
+
props.customFieldsModelType &&
|
|
1934
|
+
model.hasColumn(CustomFieldsColumnKey) &&
|
|
1935
|
+
hasPermissionToReadField(CustomFieldsColumnKey as keyof TBaseModel)
|
|
1936
|
+
) {
|
|
1937
|
+
(selectFields as Dictionary<boolean>)[CustomFieldsColumnKey] = true;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1703
1940
|
const selectMoreFields: Array<keyof TBaseModel> = props.selectMoreFields
|
|
1704
1941
|
? (Object.keys(props.selectMoreFields) as Array<keyof TBaseModel>)
|
|
1705
1942
|
: [];
|
|
@@ -1751,6 +1988,9 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1751
1988
|
currentItemsOnPage={itemsOnPage}
|
|
1752
1989
|
currentSortOrder={sortOrder}
|
|
1753
1990
|
currentFacetState={props.currentFacetState}
|
|
1991
|
+
currentColumns={
|
|
1992
|
+
columnPreferenceToJSON(effectiveColumnPreference) || undefined
|
|
1993
|
+
}
|
|
1754
1994
|
onViewChange={async (tableView: TableView | null) => {
|
|
1755
1995
|
setTableView(tableView);
|
|
1756
1996
|
|
|
@@ -1785,6 +2025,20 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1785
2025
|
(tableView.facets as JSONObject | undefined) || null,
|
|
1786
2026
|
);
|
|
1787
2027
|
}
|
|
2028
|
+
|
|
2029
|
+
/*
|
|
2030
|
+
* A saved view carries the columns it was saved with. Views
|
|
2031
|
+
* created before this existed have none, which restores the
|
|
2032
|
+
* table's default layout rather than leaving the previous
|
|
2033
|
+
* view's columns in place.
|
|
2034
|
+
*/
|
|
2035
|
+
if (isColumnCustomizationEnabled) {
|
|
2036
|
+
saveColumnPreference(
|
|
2037
|
+
columnPreferenceFromJSON(
|
|
2038
|
+
(tableView.columns as JSONObject | undefined) || null,
|
|
2039
|
+
),
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
1788
2042
|
} else {
|
|
1789
2043
|
setQuery({});
|
|
1790
2044
|
/*
|
|
@@ -1802,6 +2056,13 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1802
2056
|
if (props.onFacetStateRestored) {
|
|
1803
2057
|
props.onFacetStateRestored(null);
|
|
1804
2058
|
}
|
|
2059
|
+
|
|
2060
|
+
/*
|
|
2061
|
+
* Columns are deliberately left alone here. Clearing a saved
|
|
2062
|
+
* view means "stop filtering like that", not "throw away the
|
|
2063
|
+
* column layout I built" - which the viewer may well have set
|
|
2064
|
+
* up long before this view existed.
|
|
2065
|
+
*/
|
|
1805
2066
|
}
|
|
1806
2067
|
}}
|
|
1807
2068
|
/>
|
|
@@ -1935,6 +2196,23 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1935
2196
|
});
|
|
1936
2197
|
}
|
|
1937
2198
|
|
|
2199
|
+
/*
|
|
2200
|
+
* Only worth offering once there is a choice to make. A single-column
|
|
2201
|
+
* table has nothing to hide and nothing to reorder.
|
|
2202
|
+
*/
|
|
2203
|
+
if (isColumnCustomizationEnabled && allColumns.length > 1) {
|
|
2204
|
+
headerbuttons.push({
|
|
2205
|
+
title: "",
|
|
2206
|
+
buttonStyle: ButtonStyleType.ICON,
|
|
2207
|
+
buttonSize: ButtonSize.Small,
|
|
2208
|
+
className: "",
|
|
2209
|
+
onClick: () => {
|
|
2210
|
+
setShowColumnCustomizationModal(true);
|
|
2211
|
+
},
|
|
2212
|
+
icon: IconProp.TableCells,
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
|
|
1938
2216
|
setCardButtons(headerbuttons);
|
|
1939
2217
|
};
|
|
1940
2218
|
|
|
@@ -2108,6 +2386,14 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
2108
2386
|
serializeToTableColumns();
|
|
2109
2387
|
}, [props.columns]);
|
|
2110
2388
|
|
|
2389
|
+
/*
|
|
2390
|
+
* The viewer changed their layout, or the project's custom field
|
|
2391
|
+
* definitions finally arrived.
|
|
2392
|
+
*/
|
|
2393
|
+
useEffect(() => {
|
|
2394
|
+
serializeToTableColumns();
|
|
2395
|
+
}, [effectiveColumnPreference, customFieldColumns.columns]);
|
|
2396
|
+
|
|
2111
2397
|
const setActionSchema: VoidFunction = () => {
|
|
2112
2398
|
const permissions: Array<Permission> = PermissionUtil.getAllPermissions();
|
|
2113
2399
|
|
|
@@ -3016,36 +3302,46 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
3016
3302
|
/>
|
|
3017
3303
|
{/* Pills + input wrap row */}
|
|
3018
3304
|
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-1.5">
|
|
3019
|
-
{
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3305
|
+
{/*
|
|
3306
|
+
* Gated on label support as well as on the chips themselves: a
|
|
3307
|
+
* pill this table cannot name, apply or refill is never worth
|
|
3308
|
+
* drawing, whatever left it in state.
|
|
3309
|
+
*/}
|
|
3310
|
+
{hasLabelSupport &&
|
|
3311
|
+
selectedLabels.map((label: SearchLabelOption) => {
|
|
3312
|
+
return (
|
|
3026
3313
|
<span
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
/>
|
|
3031
|
-
<span className="max-w-[8rem] truncate">{label.name}</span>
|
|
3032
|
-
<button
|
|
3033
|
-
type="button"
|
|
3034
|
-
onMouseDown={(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
3035
|
-
e.preventDefault();
|
|
3036
|
-
}}
|
|
3037
|
-
onClick={() => {
|
|
3038
|
-
removeLabel(label.id);
|
|
3039
|
-
}}
|
|
3040
|
-
title="Remove label"
|
|
3041
|
-
aria-label={`Remove ${label.name}`}
|
|
3042
|
-
className="ml-0.5 flex-none rounded-full p-0.5 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700"
|
|
3314
|
+
key={label.id}
|
|
3315
|
+
className="inline-flex items-center gap-1 rounded-full bg-gray-50 py-0.5 pl-2 pr-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-all hover:bg-gray-100"
|
|
3316
|
+
title={`Label: ${label.name}`}
|
|
3043
3317
|
>
|
|
3044
|
-
<
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3318
|
+
<span
|
|
3319
|
+
className="h-2 w-2 flex-none rounded-full"
|
|
3320
|
+
style={{ backgroundColor: label.color }}
|
|
3321
|
+
aria-hidden="true"
|
|
3322
|
+
/>
|
|
3323
|
+
<span className="max-w-[8rem] truncate">
|
|
3324
|
+
{label.name}
|
|
3325
|
+
</span>
|
|
3326
|
+
<button
|
|
3327
|
+
type="button"
|
|
3328
|
+
onMouseDown={(
|
|
3329
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
3330
|
+
) => {
|
|
3331
|
+
e.preventDefault();
|
|
3332
|
+
}}
|
|
3333
|
+
onClick={() => {
|
|
3334
|
+
removeLabel(label.id);
|
|
3335
|
+
}}
|
|
3336
|
+
title="Remove label"
|
|
3337
|
+
aria-label={`Remove ${label.name}`}
|
|
3338
|
+
className="ml-0.5 flex-none rounded-full p-0.5 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700"
|
|
3339
|
+
>
|
|
3340
|
+
<Icon icon={IconProp.Close} className="h-3 w-3" />
|
|
3341
|
+
</button>
|
|
3342
|
+
</span>
|
|
3343
|
+
);
|
|
3344
|
+
})}
|
|
3049
3345
|
<input
|
|
3050
3346
|
ref={searchInputRef}
|
|
3051
3347
|
type="text"
|
|
@@ -3375,6 +3671,8 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
3375
3671
|
return "Watch Demo";
|
|
3376
3672
|
case IconProp.Search:
|
|
3377
3673
|
return "Search";
|
|
3674
|
+
case IconProp.TableCells:
|
|
3675
|
+
return "Columns";
|
|
3378
3676
|
default:
|
|
3379
3677
|
return "Action";
|
|
3380
3678
|
}
|
|
@@ -3581,6 +3879,133 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
3581
3879
|
return originalTitle;
|
|
3582
3880
|
};
|
|
3583
3881
|
|
|
3882
|
+
type IsPickableColumnFunction = (
|
|
3883
|
+
column: ModelTableColumn<TBaseModel>,
|
|
3884
|
+
) => boolean;
|
|
3885
|
+
|
|
3886
|
+
/*
|
|
3887
|
+
* A column the viewer cannot read is left out of the picker entirely.
|
|
3888
|
+
* Listing it would advertise a field they have no access to, and switching
|
|
3889
|
+
* it on would put that field in the select — which the API rejects for the
|
|
3890
|
+
* whole request, blanking the table.
|
|
3891
|
+
*/
|
|
3892
|
+
const isPickableColumn: IsPickableColumnFunction = (
|
|
3893
|
+
column: ModelTableColumn<TBaseModel>,
|
|
3894
|
+
): boolean => {
|
|
3895
|
+
if (column.isNotCustomizable) {
|
|
3896
|
+
return false;
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
return hasPermissionToReadColumn(column) || User.isMasterAdmin();
|
|
3900
|
+
};
|
|
3901
|
+
|
|
3902
|
+
type GetPickerEntriesFunction = (
|
|
3903
|
+
preference: ColumnPreference | null,
|
|
3904
|
+
) => Array<CustomizableColumn<TBaseModel>>;
|
|
3905
|
+
|
|
3906
|
+
const getPickerEntries: GetPickerEntriesFunction = (
|
|
3907
|
+
preference: ColumnPreference | null,
|
|
3908
|
+
): Array<CustomizableColumn<TBaseModel>> => {
|
|
3909
|
+
return getCustomizableColumns<TBaseModel>({
|
|
3910
|
+
columns: allColumns,
|
|
3911
|
+
preference: preference,
|
|
3912
|
+
}).filter((entry: CustomizableColumn<TBaseModel>) => {
|
|
3913
|
+
return isPickableColumn(entry.column);
|
|
3914
|
+
});
|
|
3915
|
+
};
|
|
3916
|
+
|
|
3917
|
+
type GetColumnSortKeyFunction = (
|
|
3918
|
+
column: ModelTableColumn<TBaseModel>,
|
|
3919
|
+
) => string | null;
|
|
3920
|
+
|
|
3921
|
+
// Mirrors how serializeToTableColumns derives the key the header sorts on.
|
|
3922
|
+
const getColumnSortKey: GetColumnSortKeyFunction = (
|
|
3923
|
+
column: ModelTableColumn<TBaseModel>,
|
|
3924
|
+
): string | null => {
|
|
3925
|
+
const key: keyof TBaseModel | null = getColumnKey(column);
|
|
3926
|
+
|
|
3927
|
+
if (!key) {
|
|
3928
|
+
return null;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
return column.selectedProperty
|
|
3932
|
+
? `${String(key)}.${column.selectedProperty}`
|
|
3933
|
+
: String(key);
|
|
3934
|
+
};
|
|
3935
|
+
|
|
3936
|
+
type OnColumnCustomizationSaveFunction = (
|
|
3937
|
+
entries: Array<CustomizableColumn<TBaseModel>>,
|
|
3938
|
+
) => void;
|
|
3939
|
+
|
|
3940
|
+
const onColumnCustomizationSave: OnColumnCustomizationSaveFunction = (
|
|
3941
|
+
entries: Array<CustomizableColumn<TBaseModel>>,
|
|
3942
|
+
): void => {
|
|
3943
|
+
const preference: ColumnPreference =
|
|
3944
|
+
buildColumnPreference<TBaseModel>(entries);
|
|
3945
|
+
|
|
3946
|
+
/*
|
|
3947
|
+
* If the layout the viewer just saved matches what the table ships with,
|
|
3948
|
+
* store nothing. Otherwise every table anyone ever opened the picker on
|
|
3949
|
+
* would be pinned to the column set of the release they opened it in, and
|
|
3950
|
+
* columns added later would arrive already stale.
|
|
3951
|
+
*/
|
|
3952
|
+
const defaultPreference: ColumnPreference =
|
|
3953
|
+
buildColumnPreference<TBaseModel>(getPickerEntries(null));
|
|
3954
|
+
|
|
3955
|
+
const isBackToDefault: boolean =
|
|
3956
|
+
JSON.stringify(preference) === JSON.stringify(defaultPreference);
|
|
3957
|
+
|
|
3958
|
+
/*
|
|
3959
|
+
* Sorting by a column that is no longer on screen leaves the viewer with
|
|
3960
|
+
* an ordering they can neither see nor undo — there is no header left to
|
|
3961
|
+
* click. Fall back to the table's own default sort.
|
|
3962
|
+
*/
|
|
3963
|
+
if (sortBy) {
|
|
3964
|
+
const hiddenIds: Set<string> = new Set(preference.hidden);
|
|
3965
|
+
|
|
3966
|
+
const isSortedColumnHidden: boolean = entries.some(
|
|
3967
|
+
(entry: CustomizableColumn<TBaseModel>) => {
|
|
3968
|
+
return (
|
|
3969
|
+
hiddenIds.has(entry.id) &&
|
|
3970
|
+
getColumnSortKey(entry.column) === String(sortBy)
|
|
3971
|
+
);
|
|
3972
|
+
},
|
|
3973
|
+
);
|
|
3974
|
+
|
|
3975
|
+
if (isSortedColumnHidden) {
|
|
3976
|
+
setSortBy((props.sortBy as keyof TBaseModel | undefined) || null);
|
|
3977
|
+
setSortOrder(props.sortOrder || SortOrder.Ascending);
|
|
3978
|
+
setCurrentPageNumber(1);
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
saveColumnPreference(isBackToDefault ? null : preference);
|
|
3983
|
+
setShowColumnCustomizationModal(false);
|
|
3984
|
+
};
|
|
3985
|
+
|
|
3986
|
+
const getColumnCustomizationModal: () => ReactElement | null =
|
|
3987
|
+
(): ReactElement | null => {
|
|
3988
|
+
if (!showColumnCustomizationModal || !isColumnCustomizationEnabled) {
|
|
3989
|
+
return null;
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
return (
|
|
3993
|
+
<ColumnCustomizationModal<TBaseModel>
|
|
3994
|
+
columns={getPickerEntries(effectiveColumnPreference)}
|
|
3995
|
+
isDefaultLayout={isEmptyColumnPreference(effectiveColumnPreference)}
|
|
3996
|
+
title={tx("Customize Columns")}
|
|
3997
|
+
onSave={onColumnCustomizationSave}
|
|
3998
|
+
onReset={() => {
|
|
3999
|
+
saveColumnPreference(null);
|
|
4000
|
+
setShowColumnCustomizationModal(false);
|
|
4001
|
+
}}
|
|
4002
|
+
onClose={() => {
|
|
4003
|
+
setShowColumnCustomizationModal(false);
|
|
4004
|
+
}}
|
|
4005
|
+
/>
|
|
4006
|
+
);
|
|
4007
|
+
};
|
|
4008
|
+
|
|
3584
4009
|
const getCardComponent: GetReactElementFunction = (): ReactElement => {
|
|
3585
4010
|
const headerButtons: Array<CardButtonSchema | ReactElement> =
|
|
3586
4011
|
getHeaderButtonsWithSearch();
|
|
@@ -3601,7 +4026,12 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
3601
4026
|
getCardTitle(props.cardProps.title || ""),
|
|
3602
4027
|
)}
|
|
3603
4028
|
>
|
|
3604
|
-
{
|
|
4029
|
+
{/*
|
|
4030
|
+
* A viewer's layout can never empty this out — applyColumnPreference
|
|
4031
|
+
* refuses to return nothing — so zero columns still means exactly
|
|
4032
|
+
* what it always did: every column was denied by permission.
|
|
4033
|
+
*/}
|
|
4034
|
+
{tableColumns.length === 0 && allColumns.length > 0 ? (
|
|
3605
4035
|
<ErrorMessage
|
|
3606
4036
|
message={`You are not authorized to view this table. You need any one of these permissions: ${PermissionHelper.getPermissionTitles(
|
|
3607
4037
|
model.getReadPermissions(),
|
|
@@ -3799,6 +4229,8 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
3799
4229
|
</div>
|
|
3800
4230
|
</Modal>
|
|
3801
4231
|
)}
|
|
4232
|
+
|
|
4233
|
+
{getColumnCustomizationModal()}
|
|
3802
4234
|
</>
|
|
3803
4235
|
);
|
|
3804
4236
|
};
|