@oneuptime/common 12.0.9 → 12.0.11
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/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* One field of a record: the column's real name and description on the left,
|
|
3
|
+
* the value on the right.
|
|
4
|
+
*
|
|
5
|
+
* This is a settings form, not a table of key/value pairs, because the keys are
|
|
6
|
+
* not the builder's to invent - they are the model's columns, and the model has
|
|
7
|
+
* already said what each one is called and what it means.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import IconProp from "../../../../Types/Icon/IconProp";
|
|
11
|
+
import AutocompleteTextInput from "../../AutocompleteTextInput/AutocompleteTextInput";
|
|
12
|
+
import Button, { ButtonStyleType } from "../../Button/Button";
|
|
13
|
+
import { ModelSchemaColumn } from "../ModelSchema";
|
|
14
|
+
import { columnTypeLabel, controlForColumn } from "./ColumnControl";
|
|
15
|
+
import {
|
|
16
|
+
ColumnValueMode,
|
|
17
|
+
ModelColumnControl,
|
|
18
|
+
ModelColumnRow,
|
|
19
|
+
changeColumnRow,
|
|
20
|
+
} from "./ColumnRow";
|
|
21
|
+
import { rowIssue } from "./ColumnRowSerialization";
|
|
22
|
+
import ColumnValueInput from "./ColumnValueInput";
|
|
23
|
+
import React, { FunctionComponent, ReactElement, useId } from "react";
|
|
24
|
+
|
|
25
|
+
export interface ComponentProps {
|
|
26
|
+
row: ModelColumnRow;
|
|
27
|
+
/** Undefined when the stored key is not a column this model describes. */
|
|
28
|
+
column: ModelSchemaColumn | undefined;
|
|
29
|
+
/** Every column id, offered as autocomplete when a key has to be retyped. */
|
|
30
|
+
knownColumnIds: Array<string>;
|
|
31
|
+
isRequired: boolean;
|
|
32
|
+
suggestions?: Array<string> | undefined;
|
|
33
|
+
/** Focused on mount, so a field added from the picker is ready to type into. */
|
|
34
|
+
autoFocus?: boolean | undefined;
|
|
35
|
+
onChange: (row: ModelColumnRow) => void;
|
|
36
|
+
onRemove: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ColumnFieldRow: FunctionComponent<ComponentProps> = (
|
|
40
|
+
props: ComponentProps,
|
|
41
|
+
): ReactElement => {
|
|
42
|
+
const labelId: string = useId();
|
|
43
|
+
const control: ModelColumnControl = controlForColumn(props.column);
|
|
44
|
+
const issue: string | null = rowIssue(props.row, props.column);
|
|
45
|
+
const isUnknownColumn: boolean = !props.column;
|
|
46
|
+
const typeLabel: string = columnTypeLabel(props.column);
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* An empty required field is worth pointing at, but only quietly: the step
|
|
50
|
+
* cannot be saved without it anyway (form validation reads the whole value),
|
|
51
|
+
* and a red row for something the builder has not reached yet reads as an
|
|
52
|
+
* error they made.
|
|
53
|
+
*/
|
|
54
|
+
const isEmptyRequired: boolean =
|
|
55
|
+
props.isRequired && props.row.text === "" && props.row.values.length === 0;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className="group relative grid grid-cols-1 items-start gap-x-4 gap-y-2 px-3 py-3 transition-colors hover:bg-gray-50/60 sm:grid-cols-[minmax(0,14rem)_minmax(0,1fr)_auto]"
|
|
60
|
+
data-testid="model-column-row"
|
|
61
|
+
>
|
|
62
|
+
{isEmptyRequired && (
|
|
63
|
+
<span
|
|
64
|
+
className="absolute left-0 top-2 bottom-2 w-0.5 rounded-r bg-amber-400"
|
|
65
|
+
aria-hidden="true"
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
|
|
69
|
+
<div className="min-w-0">
|
|
70
|
+
{isUnknownColumn ? (
|
|
71
|
+
<div>
|
|
72
|
+
<AutocompleteTextInput
|
|
73
|
+
value={props.row.columnId}
|
|
74
|
+
placeholder="Column name"
|
|
75
|
+
suggestions={props.knownColumnIds}
|
|
76
|
+
outerDivClassName="relative w-full"
|
|
77
|
+
className="block w-full rounded-md border border-amber-300 bg-white py-1.5 pl-2 pr-2 font-mono text-xs text-gray-900 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
|
78
|
+
disableSpellCheck={true}
|
|
79
|
+
onChange={(value: string) => {
|
|
80
|
+
props.onChange(changeColumnRow(props.row, { columnId: value }));
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
<span className="mt-1 inline-block rounded bg-amber-50 px-1.5 py-0.5 text-[11px] text-amber-700">
|
|
84
|
+
Not a column on this model
|
|
85
|
+
</span>
|
|
86
|
+
</div>
|
|
87
|
+
) : (
|
|
88
|
+
<>
|
|
89
|
+
<label
|
|
90
|
+
id={labelId}
|
|
91
|
+
className="block truncate text-sm font-medium text-gray-900"
|
|
92
|
+
>
|
|
93
|
+
{props.column?.title}
|
|
94
|
+
{props.isRequired && (
|
|
95
|
+
<span className="ml-0.5 text-red-500" aria-hidden="true">
|
|
96
|
+
*
|
|
97
|
+
</span>
|
|
98
|
+
)}
|
|
99
|
+
</label>
|
|
100
|
+
{/*
|
|
101
|
+
The column name and its type share a line. Stacked they made every
|
|
102
|
+
field four lines tall, and the two together are one fact: what this
|
|
103
|
+
key is called on the wire, and what shape of value it takes.
|
|
104
|
+
|
|
105
|
+
The type is dropped when it would only repeat the field's own name -
|
|
106
|
+
a column called "Color" does not need "Color" printed under it.
|
|
107
|
+
*/}
|
|
108
|
+
<span className="mt-1 flex items-center gap-1.5 text-[11px] text-gray-500">
|
|
109
|
+
<span className="truncate rounded bg-gray-100 px-1.5 py-0.5 font-mono">
|
|
110
|
+
{props.row.columnId}
|
|
111
|
+
</span>
|
|
112
|
+
{typeLabel.toLowerCase() !==
|
|
113
|
+
(props.column?.title || "").toLowerCase() && (
|
|
114
|
+
<span className="truncate text-gray-400">{typeLabel}</span>
|
|
115
|
+
)}
|
|
116
|
+
</span>
|
|
117
|
+
{/*
|
|
118
|
+
Wrapped rather than truncated. This sentence is the model's own
|
|
119
|
+
explanation of the field and is often the only thing that settles
|
|
120
|
+
what to type; cutting it off mid-word and hiding the rest behind a
|
|
121
|
+
hover is not a real answer inside a modal.
|
|
122
|
+
*/}
|
|
123
|
+
{props.column?.description && (
|
|
124
|
+
<span className="mt-1 block text-xs leading-snug text-gray-500">
|
|
125
|
+
{props.column.description}
|
|
126
|
+
</span>
|
|
127
|
+
)}
|
|
128
|
+
</>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div className="min-w-0">
|
|
133
|
+
<ColumnValueInput
|
|
134
|
+
control={control}
|
|
135
|
+
valueMode={props.row.valueMode}
|
|
136
|
+
text={props.row.text}
|
|
137
|
+
values={props.row.values}
|
|
138
|
+
placeholder={props.column?.example || props.column?.placeholder}
|
|
139
|
+
suggestions={props.suggestions}
|
|
140
|
+
ariaLabelledby={isUnknownColumn ? undefined : labelId}
|
|
141
|
+
autoFocus={props.autoFocus}
|
|
142
|
+
dataTestId={`model-column-value-${props.row.columnId}`}
|
|
143
|
+
onChange={(change: Partial<ModelColumnRow>) => {
|
|
144
|
+
props.onChange(changeColumnRow(props.row, change));
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
{props.row.valueMode === ColumnValueMode.Raw && (
|
|
149
|
+
<p className="mt-1 text-[11px] text-amber-600">
|
|
150
|
+
Kept exactly as it was saved. Editing it rewrites it in this
|
|
151
|
+
column's own type.
|
|
152
|
+
</p>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
{issue && <p className="mt-1 text-[11px] text-red-500">{issue}</p>}
|
|
156
|
+
|
|
157
|
+
{props.column?.isTenantColumn && (
|
|
158
|
+
<p className="mt-1 text-[11px] text-gray-500">
|
|
159
|
+
The workflow sets this itself and ignores what you type here.
|
|
160
|
+
</p>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div className="flex items-center pt-1">
|
|
165
|
+
<Button
|
|
166
|
+
buttonStyle={ButtonStyleType.ICON}
|
|
167
|
+
icon={IconProp.Trash}
|
|
168
|
+
title="Remove"
|
|
169
|
+
dataTestId={`model-column-remove-${props.row.columnId}`}
|
|
170
|
+
className="opacity-0 transition-opacity focus-visible:opacity-100 group-hover:opacity-100"
|
|
171
|
+
onClick={props.onRemove}
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export default ColumnFieldRow;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Which comparisons a column can be filtered by, and what to call them.
|
|
3
|
+
*
|
|
4
|
+
* The old row editor offered all fourteen operators against every column, so a
|
|
5
|
+
* Boolean column offered "starts with" and an ID column offered "greater than".
|
|
6
|
+
* Both are accepted by the form and neither means anything - `uuid ILIKE '%x%'`
|
|
7
|
+
* needs a cast Postgres will not do for you.
|
|
8
|
+
*
|
|
9
|
+
* Only the labels change here. The DictionaryFilterOperator members, and
|
|
10
|
+
* therefore every byte that reaches the server, are exactly the ones the
|
|
11
|
+
* Dictionary form already emitted.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
DICTIONARY_FILTER_OPERATOR_OPTIONS,
|
|
16
|
+
DictionaryFilterOperator,
|
|
17
|
+
DictionaryFilterOperatorOption,
|
|
18
|
+
getOperatorOption,
|
|
19
|
+
} from "../../Dictionary/DictionaryFilterOperator";
|
|
20
|
+
import { ModelColumnControl } from "./ColumnRow";
|
|
21
|
+
|
|
22
|
+
const TEXT_OPERATORS: Array<DictionaryFilterOperator> = [
|
|
23
|
+
DictionaryFilterOperator.EqualTo,
|
|
24
|
+
DictionaryFilterOperator.NotEqual,
|
|
25
|
+
DictionaryFilterOperator.Contains,
|
|
26
|
+
DictionaryFilterOperator.NotContains,
|
|
27
|
+
DictionaryFilterOperator.StartsWith,
|
|
28
|
+
DictionaryFilterOperator.EndsWith,
|
|
29
|
+
DictionaryFilterOperator.IsAnyOf,
|
|
30
|
+
DictionaryFilterOperator.IsNoneOf,
|
|
31
|
+
DictionaryFilterOperator.IsEmpty,
|
|
32
|
+
DictionaryFilterOperator.IsNotEmpty,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const NUMBER_OPERATORS: Array<DictionaryFilterOperator> = [
|
|
36
|
+
DictionaryFilterOperator.EqualTo,
|
|
37
|
+
DictionaryFilterOperator.NotEqual,
|
|
38
|
+
DictionaryFilterOperator.GreaterThan,
|
|
39
|
+
DictionaryFilterOperator.GreaterThanOrEqual,
|
|
40
|
+
DictionaryFilterOperator.LessThan,
|
|
41
|
+
DictionaryFilterOperator.LessThanOrEqual,
|
|
42
|
+
DictionaryFilterOperator.IsAnyOf,
|
|
43
|
+
DictionaryFilterOperator.IsNoneOf,
|
|
44
|
+
DictionaryFilterOperator.IsEmpty,
|
|
45
|
+
DictionaryFilterOperator.IsNotEmpty,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const DATE_OPERATORS: Array<DictionaryFilterOperator> = [
|
|
49
|
+
DictionaryFilterOperator.EqualTo,
|
|
50
|
+
DictionaryFilterOperator.NotEqual,
|
|
51
|
+
DictionaryFilterOperator.GreaterThan,
|
|
52
|
+
DictionaryFilterOperator.GreaterThanOrEqual,
|
|
53
|
+
DictionaryFilterOperator.LessThan,
|
|
54
|
+
DictionaryFilterOperator.LessThanOrEqual,
|
|
55
|
+
DictionaryFilterOperator.IsEmpty,
|
|
56
|
+
DictionaryFilterOperator.IsNotEmpty,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const IDENTIFIER_OPERATORS: Array<DictionaryFilterOperator> = [
|
|
60
|
+
DictionaryFilterOperator.EqualTo,
|
|
61
|
+
DictionaryFilterOperator.NotEqual,
|
|
62
|
+
DictionaryFilterOperator.IsAnyOf,
|
|
63
|
+
DictionaryFilterOperator.IsNoneOf,
|
|
64
|
+
DictionaryFilterOperator.IsEmpty,
|
|
65
|
+
DictionaryFilterOperator.IsNotEmpty,
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const BOOLEAN_OPERATORS: Array<DictionaryFilterOperator> = [
|
|
69
|
+
DictionaryFilterOperator.EqualTo,
|
|
70
|
+
DictionaryFilterOperator.NotEqual,
|
|
71
|
+
DictionaryFilterOperator.IsEmpty,
|
|
72
|
+
DictionaryFilterOperator.IsNotEmpty,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* A date reads far better as "is after" than as ">", and the ordering
|
|
77
|
+
* operators are the whole point of filtering on one. Anything not listed keeps
|
|
78
|
+
* the shared label from DICTIONARY_FILTER_OPERATOR_OPTIONS.
|
|
79
|
+
*/
|
|
80
|
+
const DATE_OPERATOR_LABELS: Partial<Record<DictionaryFilterOperator, string>> =
|
|
81
|
+
{
|
|
82
|
+
[DictionaryFilterOperator.EqualTo]: "is",
|
|
83
|
+
[DictionaryFilterOperator.NotEqual]: "is not",
|
|
84
|
+
[DictionaryFilterOperator.GreaterThan]: "is after",
|
|
85
|
+
[DictionaryFilterOperator.GreaterThanOrEqual]: "is on or after",
|
|
86
|
+
[DictionaryFilterOperator.LessThan]: "is before",
|
|
87
|
+
[DictionaryFilterOperator.LessThanOrEqual]: "is on or before",
|
|
88
|
+
[DictionaryFilterOperator.IsEmpty]: "is not set",
|
|
89
|
+
[DictionaryFilterOperator.IsNotEmpty]: "is set",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const BOOLEAN_OPERATOR_LABELS: Partial<
|
|
93
|
+
Record<DictionaryFilterOperator, string>
|
|
94
|
+
> = {
|
|
95
|
+
[DictionaryFilterOperator.EqualTo]: "is",
|
|
96
|
+
[DictionaryFilterOperator.NotEqual]: "is not",
|
|
97
|
+
[DictionaryFilterOperator.IsEmpty]: "is not set",
|
|
98
|
+
[DictionaryFilterOperator.IsNotEmpty]: "is set",
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const DEFAULT_OPERATOR_LABELS: Partial<
|
|
102
|
+
Record<DictionaryFilterOperator, string>
|
|
103
|
+
> = {
|
|
104
|
+
[DictionaryFilterOperator.IsEmpty]: "is not set",
|
|
105
|
+
[DictionaryFilterOperator.IsNotEmpty]: "is set",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type OperatorsForControlFunction = (
|
|
109
|
+
control: ModelColumnControl,
|
|
110
|
+
currentOperator?: DictionaryFilterOperator | undefined,
|
|
111
|
+
) => Array<DictionaryFilterOperator>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The operators offered for a column, in the order they should be listed.
|
|
115
|
+
*
|
|
116
|
+
* `currentOperator` is always included even when the type filter would drop it.
|
|
117
|
+
* A query saved before this filter existed can hold "contains" on an ID column;
|
|
118
|
+
* leaving it out of the list would render a blank dropdown, and the next
|
|
119
|
+
* unrelated edit to that row would silently rewrite the operator to equals.
|
|
120
|
+
*/
|
|
121
|
+
export const operatorsForControl: OperatorsForControlFunction = (
|
|
122
|
+
control: ModelColumnControl,
|
|
123
|
+
currentOperator?: DictionaryFilterOperator | undefined,
|
|
124
|
+
): Array<DictionaryFilterOperator> => {
|
|
125
|
+
let operators: Array<DictionaryFilterOperator>;
|
|
126
|
+
|
|
127
|
+
switch (control) {
|
|
128
|
+
case ModelColumnControl.Number:
|
|
129
|
+
operators = NUMBER_OPERATORS;
|
|
130
|
+
break;
|
|
131
|
+
case ModelColumnControl.Date:
|
|
132
|
+
operators = DATE_OPERATORS;
|
|
133
|
+
break;
|
|
134
|
+
case ModelColumnControl.ObjectId:
|
|
135
|
+
case ModelColumnControl.Color:
|
|
136
|
+
operators = IDENTIFIER_OPERATORS;
|
|
137
|
+
break;
|
|
138
|
+
case ModelColumnControl.Boolean:
|
|
139
|
+
operators = BOOLEAN_OPERATORS;
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
/*
|
|
143
|
+
* Unsupported lands here too. It is never offered in the picker, but a
|
|
144
|
+
* stored condition on such a column still gets a row, and that row should
|
|
145
|
+
* have the widest set rather than the narrowest.
|
|
146
|
+
*/
|
|
147
|
+
operators = TEXT_OPERATORS;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (currentOperator && !operators.includes(currentOperator)) {
|
|
152
|
+
return [...operators, currentOperator];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [...operators];
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export type DefaultOperatorForControlFunction = (
|
|
159
|
+
control: ModelColumnControl,
|
|
160
|
+
) => DictionaryFilterOperator;
|
|
161
|
+
|
|
162
|
+
export const defaultOperatorForControl: DefaultOperatorForControlFunction =
|
|
163
|
+
(): DictionaryFilterOperator => {
|
|
164
|
+
/*
|
|
165
|
+
* Equals for every type. It is the only operator that is right more often
|
|
166
|
+
* than not, and it is the one the value serializes without a wrapper.
|
|
167
|
+
*/
|
|
168
|
+
return DictionaryFilterOperator.EqualTo;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export type OperatorLabelForFunction = (
|
|
172
|
+
operator: DictionaryFilterOperator,
|
|
173
|
+
control: ModelColumnControl,
|
|
174
|
+
) => string;
|
|
175
|
+
|
|
176
|
+
export const operatorLabelFor: OperatorLabelForFunction = (
|
|
177
|
+
operator: DictionaryFilterOperator,
|
|
178
|
+
control: ModelColumnControl,
|
|
179
|
+
): string => {
|
|
180
|
+
const overrides: Partial<Record<DictionaryFilterOperator, string>> =
|
|
181
|
+
control === ModelColumnControl.Date
|
|
182
|
+
? DATE_OPERATOR_LABELS
|
|
183
|
+
: control === ModelColumnControl.Boolean
|
|
184
|
+
? BOOLEAN_OPERATOR_LABELS
|
|
185
|
+
: DEFAULT_OPERATOR_LABELS;
|
|
186
|
+
|
|
187
|
+
const override: string | undefined = overrides[operator];
|
|
188
|
+
|
|
189
|
+
if (override) {
|
|
190
|
+
return override;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return getOperatorOption(operator).label;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type IsKnownOperatorFunction = (value: string) => boolean;
|
|
197
|
+
|
|
198
|
+
export const isKnownOperator: IsKnownOperatorFunction = (
|
|
199
|
+
value: string,
|
|
200
|
+
): boolean => {
|
|
201
|
+
return DICTIONARY_FILTER_OPERATOR_OPTIONS.some(
|
|
202
|
+
(option: DictionaryFilterOperatorOption) => {
|
|
203
|
+
return option.operator === value;
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The row model shared by the record form and the query builder.
|
|
3
|
+
*
|
|
4
|
+
* A row is one column of the model plus the value the builder typed for it.
|
|
5
|
+
* Everything the editor knows about a row that is not the schema lives here,
|
|
6
|
+
* and the two React bodies hold no state of their own — ModelColumnEditor owns
|
|
7
|
+
* the array and hands it down, so a row can never disagree with the JSON the
|
|
8
|
+
* argument is stored as.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { DictionaryFilterOperator } from "../../Dictionary/DictionaryFilterOperator";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The kind of control a column's value is typed into.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately much smaller than TableColumnType: the builder does not need a
|
|
17
|
+
* different box for Email than for Slug, and every extra control is another
|
|
18
|
+
* thing that can render the wrong way round. What matters is the shape of the
|
|
19
|
+
* value that comes out — string, number, boolean, ISO date — because that is
|
|
20
|
+
* what ends up in the JSON the server parses.
|
|
21
|
+
*/
|
|
22
|
+
export enum ModelColumnControl {
|
|
23
|
+
Text = "Text",
|
|
24
|
+
LongText = "LongText",
|
|
25
|
+
Number = "Number",
|
|
26
|
+
Boolean = "Boolean",
|
|
27
|
+
Date = "Date",
|
|
28
|
+
ObjectId = "ObjectId",
|
|
29
|
+
Color = "Color",
|
|
30
|
+
/** Buffers, files, nested JSON, monitor steps — no row can hold one. */
|
|
31
|
+
Unsupported = "Unsupported",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* How the value in a row is being edited.
|
|
36
|
+
*
|
|
37
|
+
* Literal is the type-appropriate control. Reference is the {{ }} autocomplete,
|
|
38
|
+
* available on every column type because most creates are assembled out of an
|
|
39
|
+
* earlier step's return values. Raw is the escape hatch for a stored scalar
|
|
40
|
+
* whose JavaScript type disagrees with the column's — `{"port": "8080"}` on a
|
|
41
|
+
* numeric column — which is re-emitted exactly as it was read until the builder
|
|
42
|
+
* edits that one cell. Without Raw, opening a workflow and saving it again
|
|
43
|
+
* would quietly rewrite the stored bytes.
|
|
44
|
+
*/
|
|
45
|
+
export enum ColumnValueMode {
|
|
46
|
+
Literal = "Literal",
|
|
47
|
+
Reference = "Reference",
|
|
48
|
+
Raw = "Raw",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* One row of the editor.
|
|
53
|
+
*
|
|
54
|
+
* `text` is what the control shows and is always a string, whatever the column
|
|
55
|
+
* type — the conversion to a JSON number/boolean/null happens once, on the way
|
|
56
|
+
* out, in ColumnRowSerialization. `values` is only used by the multi-value
|
|
57
|
+
* operators (is any of / is none of), which Record mode never offers.
|
|
58
|
+
*/
|
|
59
|
+
export interface ModelColumnRow {
|
|
60
|
+
/**
|
|
61
|
+
* This row's identity for React, fixed when the row is created.
|
|
62
|
+
*
|
|
63
|
+
* It cannot be the column name: an unknown column is edited in a text box, so
|
|
64
|
+
* keying on the name would give the row a new identity on every keystroke,
|
|
65
|
+
* unmount it mid-word and take the focused input with it.
|
|
66
|
+
*/
|
|
67
|
+
key: string;
|
|
68
|
+
/** The column name as it will be written into the JSON object. */
|
|
69
|
+
columnId: string;
|
|
70
|
+
operator: DictionaryFilterOperator;
|
|
71
|
+
valueMode: ColumnValueMode;
|
|
72
|
+
text: string;
|
|
73
|
+
values: Array<string>;
|
|
74
|
+
/**
|
|
75
|
+
* True for a blank row the editor opened with because the model says a create
|
|
76
|
+
* must supply this column. Seeded rows serialize away while they are empty,
|
|
77
|
+
* so an untouched Create One still reads as "nothing set" to form validation
|
|
78
|
+
* and to the graph linter.
|
|
79
|
+
*/
|
|
80
|
+
isSeeded: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The value exactly as it was read from storage, kept only for
|
|
83
|
+
* ColumnValueMode.Raw so that round-tripping is byte-for-byte.
|
|
84
|
+
*/
|
|
85
|
+
rawValue?: unknown;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* A counter rather than a random id, so a row's identity is reproducible and
|
|
90
|
+
* two editors mounted in the same page can never collide.
|
|
91
|
+
*/
|
|
92
|
+
let nextRowKey: number = 0;
|
|
93
|
+
|
|
94
|
+
export type MakeColumnRowFunction = (
|
|
95
|
+
row: Partial<ModelColumnRow> & { columnId: string },
|
|
96
|
+
) => ModelColumnRow;
|
|
97
|
+
|
|
98
|
+
export const makeColumnRow: MakeColumnRowFunction = (
|
|
99
|
+
row: Partial<ModelColumnRow> & { columnId: string },
|
|
100
|
+
): ModelColumnRow => {
|
|
101
|
+
nextRowKey = nextRowKey + 1;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
key: row.key || `row-${nextRowKey}`,
|
|
105
|
+
columnId: row.columnId,
|
|
106
|
+
operator: row.operator || DictionaryFilterOperator.EqualTo,
|
|
107
|
+
valueMode: row.valueMode || ColumnValueMode.Literal,
|
|
108
|
+
text: row.text === undefined ? "" : row.text,
|
|
109
|
+
values: row.values || [],
|
|
110
|
+
isSeeded: Boolean(row.isSeeded),
|
|
111
|
+
...(row.rawValue === undefined ? {} : { rawValue: row.rawValue }),
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A row with some of its fields changed.
|
|
117
|
+
*
|
|
118
|
+
* Every edit goes through here rather than through several separate callbacks,
|
|
119
|
+
* because two callbacks fired from one event both close over the same render's
|
|
120
|
+
* row and the second silently discards the first. That is how editing a raw
|
|
121
|
+
* cell came to change the box on screen without changing what was stored.
|
|
122
|
+
*
|
|
123
|
+
* Leaving Raw drops the stored original: from that point the builder's own
|
|
124
|
+
* value is what the row means, in the column's own type.
|
|
125
|
+
*/
|
|
126
|
+
export type ChangeColumnRowFunction = (
|
|
127
|
+
row: ModelColumnRow,
|
|
128
|
+
change: Partial<
|
|
129
|
+
Pick<
|
|
130
|
+
ModelColumnRow,
|
|
131
|
+
"columnId" | "operator" | "valueMode" | "text" | "values"
|
|
132
|
+
>
|
|
133
|
+
>,
|
|
134
|
+
) => ModelColumnRow;
|
|
135
|
+
|
|
136
|
+
export const changeColumnRow: ChangeColumnRowFunction = (
|
|
137
|
+
row: ModelColumnRow,
|
|
138
|
+
change: Partial<
|
|
139
|
+
Pick<
|
|
140
|
+
ModelColumnRow,
|
|
141
|
+
"columnId" | "operator" | "valueMode" | "text" | "values"
|
|
142
|
+
>
|
|
143
|
+
>,
|
|
144
|
+
): ModelColumnRow => {
|
|
145
|
+
const next: ModelColumnRow = {
|
|
146
|
+
...row,
|
|
147
|
+
...change,
|
|
148
|
+
// Any edit means the row is no longer just a blank the editor opened with.
|
|
149
|
+
isSeeded: false,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (next.valueMode !== ColumnValueMode.Raw) {
|
|
153
|
+
delete next.rawValue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return next;
|
|
157
|
+
};
|