@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,163 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The record body: every field this step writes, as a form.
|
|
3
|
+
*
|
|
4
|
+
* Fully controlled - the rows live in ModelColumnEditor, so what is on screen
|
|
5
|
+
* and what is stored on the argument can never drift apart.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import IconProp from "../../../../Types/Icon/IconProp";
|
|
9
|
+
import Icon from "../../Icon/Icon";
|
|
10
|
+
import {
|
|
11
|
+
ModelSchemaColumn,
|
|
12
|
+
findColumn,
|
|
13
|
+
requiredWritableColumns,
|
|
14
|
+
} from "../ModelSchema";
|
|
15
|
+
import AddColumnDropdown from "./AddColumnDropdown";
|
|
16
|
+
import { isOfferableColumn } from "./ColumnControl";
|
|
17
|
+
import ColumnFieldRow from "./ColumnFieldRow";
|
|
18
|
+
import { ModelColumnRow, makeColumnRow } from "./ColumnRow";
|
|
19
|
+
import React, { FunctionComponent, ReactElement, useState } from "react";
|
|
20
|
+
|
|
21
|
+
export interface ComponentProps {
|
|
22
|
+
rows: Array<ModelColumnRow>;
|
|
23
|
+
columns: Array<ModelSchemaColumn>;
|
|
24
|
+
suggestions?: Array<string> | undefined;
|
|
25
|
+
onChange: (rows: Array<ModelColumnRow>) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ModelRecordForm: FunctionComponent<ComponentProps> = (
|
|
29
|
+
props: ComponentProps,
|
|
30
|
+
): ReactElement => {
|
|
31
|
+
/*
|
|
32
|
+
* The row just added from the picker, so its value control can take focus.
|
|
33
|
+
* Without it the picker remounts to clear its own selection and focus falls
|
|
34
|
+
* to the document body, which for a keyboard user means starting again from
|
|
35
|
+
* the top of the panel.
|
|
36
|
+
*/
|
|
37
|
+
const [justAddedKey, setJustAddedKey] = useState<string>("");
|
|
38
|
+
|
|
39
|
+
const requiredColumnIds: Array<string> = requiredWritableColumns(
|
|
40
|
+
props.columns,
|
|
41
|
+
).map((column: ModelSchemaColumn) => {
|
|
42
|
+
return column.id;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const usedColumnIds: Array<string> = props.rows.map((row: ModelColumnRow) => {
|
|
46
|
+
return row.columnId;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const offerableColumns: Array<ModelSchemaColumn> = props.columns.filter(
|
|
50
|
+
(column: ModelSchemaColumn) => {
|
|
51
|
+
return isOfferableColumn(column) && !usedColumnIds.includes(column.id);
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Named so the builder is not left wondering where a column went. Relations
|
|
57
|
+
* and blobs cannot be a single box, and saying which ones is kinder than
|
|
58
|
+
* silently offering a shorter list than the model has.
|
|
59
|
+
*/
|
|
60
|
+
const unofferableColumnTitles: Array<string> = props.columns
|
|
61
|
+
.filter((column: ModelSchemaColumn) => {
|
|
62
|
+
return !isOfferableColumn(column) && !column.isTenantColumn;
|
|
63
|
+
})
|
|
64
|
+
.map((column: ModelSchemaColumn) => {
|
|
65
|
+
return column.title;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const knownColumnIds: Array<string> = props.columns.map(
|
|
69
|
+
(column: ModelSchemaColumn) => {
|
|
70
|
+
return column.id;
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
type ReplaceRowFunction = (index: number, row: ModelColumnRow) => void;
|
|
75
|
+
|
|
76
|
+
const replaceRow: ReplaceRowFunction = (
|
|
77
|
+
index: number,
|
|
78
|
+
row: ModelColumnRow,
|
|
79
|
+
): void => {
|
|
80
|
+
const next: Array<ModelColumnRow> = [...props.rows];
|
|
81
|
+
next[index] = row;
|
|
82
|
+
props.onChange(next);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div>
|
|
87
|
+
<div className="overflow-hidden rounded-md border border-gray-200 bg-white">
|
|
88
|
+
{props.rows.length === 0 ? (
|
|
89
|
+
<div className="px-6 py-10 text-center">
|
|
90
|
+
<Icon
|
|
91
|
+
icon={IconProp.Database}
|
|
92
|
+
className="mx-auto h-6 w-6 text-gray-300"
|
|
93
|
+
/>
|
|
94
|
+
<p className="mt-2 text-sm font-medium text-gray-700">
|
|
95
|
+
No fields set yet
|
|
96
|
+
</p>
|
|
97
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
98
|
+
Pick a field below to start building this record.
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
) : (
|
|
102
|
+
<div className="divide-y divide-gray-100">
|
|
103
|
+
{props.rows.map((row: ModelColumnRow, index: number) => {
|
|
104
|
+
return (
|
|
105
|
+
<ColumnFieldRow
|
|
106
|
+
key={row.key}
|
|
107
|
+
row={row}
|
|
108
|
+
column={findColumn(props.columns, row.columnId)}
|
|
109
|
+
knownColumnIds={knownColumnIds}
|
|
110
|
+
isRequired={requiredColumnIds.includes(row.columnId)}
|
|
111
|
+
suggestions={props.suggestions}
|
|
112
|
+
autoFocus={row.key === justAddedKey}
|
|
113
|
+
onChange={(nextRow: ModelColumnRow) => {
|
|
114
|
+
replaceRow(index, nextRow);
|
|
115
|
+
}}
|
|
116
|
+
onRemove={() => {
|
|
117
|
+
props.onChange(
|
|
118
|
+
props.rows.filter((_row: ModelColumnRow, i: number) => {
|
|
119
|
+
return i !== index;
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
})}
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
<div className="border-t border-gray-100 bg-gray-50/40 px-3 py-2.5">
|
|
130
|
+
<AddColumnDropdown
|
|
131
|
+
columns={offerableColumns}
|
|
132
|
+
requiredColumnIds={requiredColumnIds}
|
|
133
|
+
placeholder="Add a field..."
|
|
134
|
+
allowCustomColumn={true}
|
|
135
|
+
dataTestId="model-column-add"
|
|
136
|
+
onAdd={(columnId: string) => {
|
|
137
|
+
const added: ModelColumnRow = makeColumnRow({
|
|
138
|
+
columnId: columnId,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
setJustAddedKey(added.key);
|
|
142
|
+
props.onChange([...props.rows, added]);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{unofferableColumnTitles.length > 0 && (
|
|
149
|
+
<p className="mt-1.5 text-xs text-gray-400">
|
|
150
|
+
{unofferableColumnTitles.slice(0, 3).join(", ")}
|
|
151
|
+
{unofferableColumnTitles.length > 3
|
|
152
|
+
? ` and ${unofferableColumnTitles.length - 3} other field${
|
|
153
|
+
unofferableColumnTitles.length - 3 === 1 ? "" : "s"
|
|
154
|
+
}`
|
|
155
|
+
: ""}{" "}
|
|
156
|
+
can only be set with Edit as JSON.
|
|
157
|
+
</p>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export default ModelRecordForm;
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* A
|
|
2
|
+
* A schema-driven editor for the two workflow arguments that are keyed on a
|
|
3
3
|
* model's columns: the Query that picks which records a component acts on,
|
|
4
4
|
* and the record of values a create/update writes.
|
|
5
5
|
*
|
|
6
6
|
* Both were bare JSON editors, with the column names — and, for a query, the
|
|
7
|
-
* operator syntax — left entirely to memory. The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* operator syntax — left entirely to memory. The first attempt at fixing that
|
|
8
|
+
* borrowed the generic key/value row editor, which asked for a column name as
|
|
9
|
+
* free text and then asked, in a dropdown next to it, whether the value was
|
|
10
|
+
* Text, Number or Boolean. Both questions were already answered:
|
|
11
|
+
* /model-schema/:tableName says that `color` is a Color and `name` is a Name,
|
|
12
|
+
* so the builder could only agree with the schema or be wrong.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* So the rows are generated from the schema instead. A record is a form of the
|
|
15
|
+
* model's own fields, with their real titles and descriptions; a query is a
|
|
16
|
+
* list of conditions whose comparisons are filtered by the column's type. The
|
|
17
|
+
* JSON escape hatch stays for the values rows cannot express, and now it
|
|
18
|
+
* re-reads what was typed in it rather than discarding it.
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
import CodeEditor from "../CodeEditor/CodeEditor";
|
|
19
22
|
import ComponentLoader from "../ComponentLoader/ComponentLoader";
|
|
20
|
-
import
|
|
21
|
-
|
|
23
|
+
import {
|
|
24
|
+
DictionaryEntryValue,
|
|
25
|
+
getOperatorOption,
|
|
26
|
+
} from "../Dictionary/DictionaryFilterOperator";
|
|
22
27
|
import Icon from "../Icon/Icon";
|
|
23
28
|
import {
|
|
24
29
|
ModelSchemaAccess,
|
|
@@ -28,6 +33,14 @@ import {
|
|
|
28
33
|
requiredWritableColumns,
|
|
29
34
|
useModelSchema,
|
|
30
35
|
} from "./ModelSchema";
|
|
36
|
+
import { ModelColumnRow } from "./ColumnEditor/ColumnRow";
|
|
37
|
+
import {
|
|
38
|
+
rowsFromParsedValue,
|
|
39
|
+
seedRequiredRows,
|
|
40
|
+
serializeColumnRows,
|
|
41
|
+
} from "./ColumnEditor/ColumnRowSerialization";
|
|
42
|
+
import ModelQueryBuilder from "./ColumnEditor/ModelQueryBuilder";
|
|
43
|
+
import ModelRecordForm from "./ColumnEditor/ModelRecordForm";
|
|
31
44
|
import CodeType from "../../../Types/Code/CodeType";
|
|
32
45
|
import Dictionary from "../../../Types/Dictionary";
|
|
33
46
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
@@ -36,6 +49,7 @@ import { maskTemplateExpressions } from "../../../Types/Workflow/TemplateSyntax"
|
|
|
36
49
|
import React, {
|
|
37
50
|
FunctionComponent,
|
|
38
51
|
ReactElement,
|
|
52
|
+
useEffect,
|
|
39
53
|
useMemo,
|
|
40
54
|
useState,
|
|
41
55
|
} from "react";
|
|
@@ -47,6 +61,18 @@ export enum ModelColumnEditorMode {
|
|
|
47
61
|
Record = "Record",
|
|
48
62
|
}
|
|
49
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Whether the record being edited creates a row or updates one.
|
|
66
|
+
*
|
|
67
|
+
* It decides one thing: whether the editor opens with a blank row for every
|
|
68
|
+
* column a create must supply. An update legitimately writes a single column,
|
|
69
|
+
* so seeding those rows there presents six fields where the builder wanted one.
|
|
70
|
+
*/
|
|
71
|
+
export enum RecordIntent {
|
|
72
|
+
Create = "Create",
|
|
73
|
+
Update = "Update",
|
|
74
|
+
}
|
|
75
|
+
|
|
50
76
|
export interface ComponentProps {
|
|
51
77
|
tableName: string;
|
|
52
78
|
mode: ModelColumnEditorMode;
|
|
@@ -55,6 +81,8 @@ export interface ComponentProps {
|
|
|
55
81
|
error?: string | undefined;
|
|
56
82
|
placeholder?: string | undefined;
|
|
57
83
|
tabIndex?: number | undefined;
|
|
84
|
+
/** Record mode only; ignored for a query. Defaults to Create. */
|
|
85
|
+
recordIntent?: RecordIntent | undefined;
|
|
58
86
|
/*
|
|
59
87
|
* Offered in any row whose column has no suggestions of its own - the other
|
|
60
88
|
* steps' return values and the workflow's variables. The row editor replaced
|
|
@@ -356,55 +384,30 @@ const ModelColumnEditor: FunctionComponent<ComponentProps> = (
|
|
|
356
384
|
const [viewMode, setViewMode] = useState<ViewMode>("builder");
|
|
357
385
|
|
|
358
386
|
/*
|
|
359
|
-
* The
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* render after the schema resolves — the loader below returns early until
|
|
363
|
-
* then.
|
|
387
|
+
* The rows are the editor's own state, and both bodies are fully controlled
|
|
388
|
+
* from here. Null until the schema resolves, because a row cannot be built
|
|
389
|
+
* before it is known what kind of column it is for.
|
|
364
390
|
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
391
|
+
* Nothing in the seeding path calls props.onChange. That matters more than it
|
|
392
|
+
* looks: this component is mounted every time someone opens a step's
|
|
393
|
+
* settings, and a change fired during mount would rewrite the stored bytes of
|
|
394
|
+
* every workflow just for being looked at.
|
|
369
395
|
*/
|
|
370
|
-
const
|
|
371
|
-
const stored: Dictionary<DictionaryEntryValue> = (initial.parsed ||
|
|
372
|
-
{}) as Dictionary<DictionaryEntryValue>;
|
|
373
|
-
|
|
374
|
-
if (isQuery || !schema.columns) {
|
|
375
|
-
return stored;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const seeded: Dictionary<DictionaryEntryValue> = { ...stored };
|
|
379
|
-
|
|
380
|
-
for (const column of requiredWritableColumns(schema.columns)) {
|
|
381
|
-
if (seeded[column.id] === undefined) {
|
|
382
|
-
seeded[column.id] = "";
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return seeded;
|
|
387
|
-
}, [schema.columns, isQuery]);
|
|
396
|
+
const [rows, setRows] = useState<Array<ModelColumnRow> | null>(null);
|
|
388
397
|
|
|
389
398
|
/*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* which answers "what goes in this box" better than any prose in the sidebar.
|
|
399
|
+
* Why the switch back from JSON is refused, when it is. Empty means it is
|
|
400
|
+
* allowed.
|
|
393
401
|
*/
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
for (const column of schema.columns || []) {
|
|
398
|
-
const hint: string | undefined = column.example || column.placeholder;
|
|
399
|
-
|
|
400
|
-
if (hint) {
|
|
401
|
-
placeholders[column.id] = hint;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return placeholders;
|
|
406
|
-
}, [schema.columns]);
|
|
402
|
+
const [returnToRowsBlockedBy, setReturnToRowsBlockedBy] = useState<
|
|
403
|
+
Array<string>
|
|
404
|
+
>([]);
|
|
407
405
|
|
|
406
|
+
/*
|
|
407
|
+
* Whether the value can be shown as rows at all. Read from the value as it
|
|
408
|
+
* was loaded, deliberately: the decision has to be stable, or the editor
|
|
409
|
+
* would change shape under the builder's hands as they type.
|
|
410
|
+
*/
|
|
408
411
|
const compatibility: CompatibilityResult = useMemo(() => {
|
|
409
412
|
return classifyColumnValueCompatibility(
|
|
410
413
|
initial.parsed,
|
|
@@ -414,13 +417,46 @@ const ModelColumnEditor: FunctionComponent<ComponentProps> = (
|
|
|
414
417
|
);
|
|
415
418
|
}, [schema.columns, props.mode]);
|
|
416
419
|
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
});
|
|
421
|
-
}, [schema.columns]);
|
|
420
|
+
const columns: Array<ModelSchemaColumn> = schema.columns || [];
|
|
421
|
+
|
|
422
|
+
type BuildRowsFunction = (parsed: JSONObject | null) => Array<ModelColumnRow>;
|
|
422
423
|
|
|
423
|
-
|
|
424
|
+
/*
|
|
425
|
+
* A create opens with a blank row for every column it must be given, so the
|
|
426
|
+
* shape of the record is visible before anything is typed. Seeded rows hold
|
|
427
|
+
* "" and are dropped on the way out, so an untouched Create One still reads
|
|
428
|
+
* as empty to form validation and to the graph linter.
|
|
429
|
+
*
|
|
430
|
+
* Used by the JSON view's return trip as well as by the first load - built
|
|
431
|
+
* only on the way in, those rows disappeared the first time anyone looked at
|
|
432
|
+
* the JSON, and a create that had been read once no longer showed what it
|
|
433
|
+
* needed.
|
|
434
|
+
*/
|
|
435
|
+
const buildRows: BuildRowsFunction = (
|
|
436
|
+
parsed: JSONObject | null,
|
|
437
|
+
): Array<ModelColumnRow> => {
|
|
438
|
+
const storedRows: Array<ModelColumnRow> = rowsFromParsedValue(
|
|
439
|
+
parsed,
|
|
440
|
+
columns,
|
|
441
|
+
props.mode,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const shouldSeed: boolean =
|
|
445
|
+
!isQuery &&
|
|
446
|
+
(props.recordIntent || RecordIntent.Create) === RecordIntent.Create;
|
|
447
|
+
|
|
448
|
+
return shouldSeed ? seedRequiredRows(storedRows, columns) : storedRows;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
useEffect(() => {
|
|
452
|
+
if (schema.isLoading || rows !== null) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
setRows(buildRows(initial.parsed));
|
|
457
|
+
}, [schema.isLoading, schema.columns]);
|
|
458
|
+
|
|
459
|
+
if (schema.isLoading || rows === null) {
|
|
424
460
|
return <ComponentLoader />;
|
|
425
461
|
}
|
|
426
462
|
|
|
@@ -431,30 +467,130 @@ const ModelColumnEditor: FunctionComponent<ComponentProps> = (
|
|
|
431
467
|
|
|
432
468
|
const onJsonChange: OnJsonChangeFunction = (value: string): void => {
|
|
433
469
|
setJsonText(value);
|
|
470
|
+
/*
|
|
471
|
+
* Any block on returning to rows was about the previous text, so it is
|
|
472
|
+
* re-decided when the switch is next attempted rather than left standing.
|
|
473
|
+
*/
|
|
474
|
+
setReturnToRowsBlockedBy([]);
|
|
434
475
|
props.onChange(value);
|
|
435
476
|
};
|
|
436
477
|
|
|
437
|
-
type OnRowsChangeFunction = (value:
|
|
478
|
+
type OnRowsChangeFunction = (value: Array<ModelColumnRow>) => void;
|
|
438
479
|
|
|
439
480
|
const onRowsChange: OnRowsChangeFunction = (
|
|
440
|
-
value:
|
|
481
|
+
value: Array<ModelColumnRow>,
|
|
441
482
|
): void => {
|
|
442
|
-
|
|
483
|
+
setRows(value);
|
|
484
|
+
|
|
485
|
+
const asJson: string = serializeColumnRows(value, columns, props.mode);
|
|
443
486
|
setJsonText(asJson);
|
|
444
487
|
props.onChange(asJson);
|
|
445
488
|
};
|
|
446
489
|
|
|
490
|
+
type ShowJsonFunction = () => void;
|
|
491
|
+
|
|
492
|
+
const showJson: ShowJsonFunction = (): void => {
|
|
493
|
+
/*
|
|
494
|
+
* What is shown is what is stored. Handing the code editor the text the
|
|
495
|
+
* argument held before the rows were edited would be the same data loss as
|
|
496
|
+
* the other direction, one step later.
|
|
497
|
+
*/
|
|
498
|
+
setJsonText(serializeColumnRows(rows, columns, props.mode));
|
|
499
|
+
setViewMode("json");
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
type ShowRowsFunction = () => void;
|
|
503
|
+
|
|
504
|
+
const showRows: ShowRowsFunction = (): void => {
|
|
505
|
+
/*
|
|
506
|
+
* The JSON is re-read here, which is the fix for a real bug: the rows used
|
|
507
|
+
* to be seeded once and never again, so editing as JSON, adding a key and
|
|
508
|
+
* switching back silently dropped the key, and the next keystroke in any
|
|
509
|
+
* row overwrote the argument with the pre-edit content.
|
|
510
|
+
*/
|
|
511
|
+
const edited: ParsedQuery = normalizeInitialValue(jsonText);
|
|
512
|
+
const editedCompatibility: CompatibilityResult =
|
|
513
|
+
classifyColumnValueCompatibility(
|
|
514
|
+
edited.parsed,
|
|
515
|
+
columns,
|
|
516
|
+
props.mode,
|
|
517
|
+
edited.hasTemplateExpressions,
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
if (!editedCompatibility.compatible) {
|
|
521
|
+
// Refused rather than silently losing the edit.
|
|
522
|
+
setReturnToRowsBlockedBy(editedCompatibility.reasons);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
setReturnToRowsBlockedBy([]);
|
|
527
|
+
setRows(buildRows(edited.parsed));
|
|
528
|
+
setViewMode("builder");
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const filledRowCount: number = rows.filter((row: ModelColumnRow) => {
|
|
532
|
+
if (row.columnId === "") {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/*
|
|
537
|
+
* "is set" and "is not set" carry no value by design, so counting only rows
|
|
538
|
+
* that hold one reported a query with three real conditions as having none.
|
|
539
|
+
*/
|
|
540
|
+
if (isQuery && getOperatorOption(row.operator).hidesValueInput) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return row.text !== "" || row.values.length > 0;
|
|
545
|
+
}).length;
|
|
546
|
+
|
|
547
|
+
/*
|
|
548
|
+
* Recomputed from the rows on screen rather than from the value as it was
|
|
549
|
+
* loaded, so fixing the column name that caused the warning also clears it.
|
|
550
|
+
* Read from the frozen initial value, the "id isn't a known column" note
|
|
551
|
+
* stayed up after the builder had corrected it to "_id", which reads as the
|
|
552
|
+
* editor not having noticed.
|
|
553
|
+
*
|
|
554
|
+
* Only the non-blocking half is live. Whether the value can be rows at all is
|
|
555
|
+
* decided once, above.
|
|
556
|
+
*/
|
|
557
|
+
const unknownColumnWarnings: Array<string> = columns.length
|
|
558
|
+
? rows
|
|
559
|
+
.filter((row: ModelColumnRow) => {
|
|
560
|
+
return row.columnId !== "" && !findColumn(columns, row.columnId);
|
|
561
|
+
})
|
|
562
|
+
.map((row: ModelColumnRow) => {
|
|
563
|
+
return `"${row.columnId}" isn't a known column on this model.`;
|
|
564
|
+
})
|
|
565
|
+
: [];
|
|
566
|
+
|
|
567
|
+
const emptyRequiredCount: number = isQuery
|
|
568
|
+
? 0
|
|
569
|
+
: requiredWritableColumns(columns).filter((column: ModelSchemaColumn) => {
|
|
570
|
+
return rows.some((row: ModelColumnRow) => {
|
|
571
|
+
return row.columnId === column.id && row.text === "";
|
|
572
|
+
});
|
|
573
|
+
}).length;
|
|
574
|
+
|
|
575
|
+
const noun: string = isQuery ? "condition" : "field";
|
|
576
|
+
|
|
447
577
|
return (
|
|
448
578
|
<div>
|
|
449
579
|
{schema.error && (
|
|
450
|
-
<
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
580
|
+
<div className="mb-2 flex items-start gap-2 rounded-md border border-red-200 bg-red-50/60 px-3 py-2.5">
|
|
581
|
+
<Icon
|
|
582
|
+
icon={IconProp.Alert}
|
|
583
|
+
className="mt-0.5 h-3.5 w-3.5 shrink-0 text-red-500"
|
|
584
|
+
/>
|
|
585
|
+
<p className="text-sm text-red-700">
|
|
586
|
+
Couldn't load this model's columns ({schema.error}). You
|
|
587
|
+
can still write {isQuery ? "the query" : "these fields"} as JSON.
|
|
588
|
+
</p>
|
|
589
|
+
</div>
|
|
454
590
|
)}
|
|
455
591
|
|
|
456
592
|
{isLockedToJson && compatibility.reasons.length > 0 && (
|
|
457
|
-
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50
|
|
593
|
+
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50/60 px-3 py-2.5">
|
|
458
594
|
<p className="text-sm text-amber-800">Editing as JSON, because:</p>
|
|
459
595
|
<ul className="mt-1 list-disc pl-5 text-sm text-amber-700">
|
|
460
596
|
{compatibility.reasons.map((reason: string, i: number) => {
|
|
@@ -464,58 +600,106 @@ const ModelColumnEditor: FunctionComponent<ComponentProps> = (
|
|
|
464
600
|
</div>
|
|
465
601
|
)}
|
|
466
602
|
|
|
467
|
-
{!isLockedToJson &&
|
|
468
|
-
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50
|
|
603
|
+
{!isLockedToJson && unknownColumnWarnings.length > 0 && (
|
|
604
|
+
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50/60 px-3 py-2.5">
|
|
469
605
|
<ul className="list-disc pl-5 text-sm text-amber-700">
|
|
470
|
-
{
|
|
606
|
+
{unknownColumnWarnings.map((reason: string, i: number) => {
|
|
471
607
|
return <li key={i}>{reason}</li>;
|
|
472
608
|
})}
|
|
473
609
|
</ul>
|
|
474
610
|
</div>
|
|
475
611
|
)}
|
|
476
612
|
|
|
477
|
-
{
|
|
478
|
-
<
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
/>
|
|
490
|
-
) : (
|
|
491
|
-
<CodeEditor
|
|
492
|
-
type={CodeType.JSON}
|
|
493
|
-
tabIndex={props.tabIndex}
|
|
494
|
-
error={props.error}
|
|
495
|
-
initialValue={initial.text}
|
|
496
|
-
value={jsonText}
|
|
497
|
-
placeholder={props.placeholder}
|
|
498
|
-
onChange={onJsonChange}
|
|
499
|
-
/>
|
|
613
|
+
{returnToRowsBlockedBy.length > 0 && (
|
|
614
|
+
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50/60 px-3 py-2.5">
|
|
615
|
+
<p className="text-sm text-amber-800">
|
|
616
|
+
This can't go back to {isQuery ? "conditions" : "fields"},
|
|
617
|
+
because:
|
|
618
|
+
</p>
|
|
619
|
+
<ul className="mt-1 list-disc pl-5 text-sm text-amber-700">
|
|
620
|
+
{returnToRowsBlockedBy.map((reason: string, i: number) => {
|
|
621
|
+
return <li key={i}>{reason}</li>;
|
|
622
|
+
})}
|
|
623
|
+
</ul>
|
|
624
|
+
</div>
|
|
500
625
|
)}
|
|
501
626
|
|
|
502
|
-
|
|
503
|
-
<
|
|
627
|
+
<div className="mb-2 flex items-center justify-between gap-2">
|
|
628
|
+
<p className="text-xs text-gray-500">
|
|
629
|
+
{effectiveMode === "builder" ? (
|
|
630
|
+
<>
|
|
631
|
+
{filledRowCount} {noun}
|
|
632
|
+
{filledRowCount === 1 ? "" : "s"} set
|
|
633
|
+
{emptyRequiredCount > 0 && (
|
|
634
|
+
<span className="text-amber-600">
|
|
635
|
+
{" "}
|
|
636
|
+
· {emptyRequiredCount} required field
|
|
637
|
+
{emptyRequiredCount === 1 ? "" : "s"} still empty
|
|
638
|
+
</span>
|
|
639
|
+
)}
|
|
640
|
+
</>
|
|
641
|
+
) : (
|
|
642
|
+
<>Editing the raw JSON this step will send.</>
|
|
643
|
+
)}
|
|
644
|
+
</p>
|
|
645
|
+
|
|
646
|
+
{!isLockedToJson && (
|
|
504
647
|
<button
|
|
505
648
|
type="button"
|
|
506
|
-
|
|
649
|
+
data-testid="model-column-json-toggle"
|
|
650
|
+
className="inline-flex items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 hover:border-gray-300 hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
|
507
651
|
onClick={() => {
|
|
508
|
-
|
|
652
|
+
if (effectiveMode === "builder") {
|
|
653
|
+
showJson();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
showRows();
|
|
509
658
|
}}
|
|
510
659
|
>
|
|
511
|
-
<Icon
|
|
660
|
+
<Icon
|
|
661
|
+
icon={
|
|
662
|
+
effectiveMode === "builder"
|
|
663
|
+
? IconProp.Code
|
|
664
|
+
: IconProp.ListBullet
|
|
665
|
+
}
|
|
666
|
+
className="h-3.5 w-3.5 text-gray-500"
|
|
667
|
+
/>
|
|
512
668
|
{effectiveMode === "builder"
|
|
513
669
|
? "Edit as JSON"
|
|
514
670
|
: isQuery
|
|
515
671
|
? "Back to conditions"
|
|
516
672
|
: "Back to fields"}
|
|
517
673
|
</button>
|
|
518
|
-
|
|
674
|
+
)}
|
|
675
|
+
</div>
|
|
676
|
+
|
|
677
|
+
{effectiveMode === "builder" ? (
|
|
678
|
+
isQuery ? (
|
|
679
|
+
<ModelQueryBuilder
|
|
680
|
+
rows={rows}
|
|
681
|
+
columns={columns}
|
|
682
|
+
suggestions={props.valueSuggestions}
|
|
683
|
+
onChange={onRowsChange}
|
|
684
|
+
/>
|
|
685
|
+
) : (
|
|
686
|
+
<ModelRecordForm
|
|
687
|
+
rows={rows}
|
|
688
|
+
columns={columns}
|
|
689
|
+
suggestions={props.valueSuggestions}
|
|
690
|
+
onChange={onRowsChange}
|
|
691
|
+
/>
|
|
692
|
+
)
|
|
693
|
+
) : (
|
|
694
|
+
<CodeEditor
|
|
695
|
+
type={CodeType.JSON}
|
|
696
|
+
tabIndex={props.tabIndex}
|
|
697
|
+
error={props.error}
|
|
698
|
+
initialValue={initial.text}
|
|
699
|
+
value={jsonText}
|
|
700
|
+
placeholder={props.placeholder}
|
|
701
|
+
onChange={onJsonChange}
|
|
702
|
+
/>
|
|
519
703
|
)}
|
|
520
704
|
|
|
521
705
|
{props.error && effectiveMode === "builder" && (
|