├── .github ├── dependabot.yml └── workflows │ ├── codesee-arch-diagram.yml │ ├── lint.yml │ └── npm-publish.yml ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── babel.config.js ├── eslint.config.js ├── generated-sources └── api │ ├── .gitignore │ ├── .npmignore │ ├── .openapi-generator-ignore │ ├── .openapi-generator │ ├── FILES │ └── VERSION │ ├── README.md │ ├── package.json │ ├── src │ ├── apis │ │ ├── APIKeyApi.ts │ │ ├── BillingAccountApi.ts │ │ ├── ConnectionApi.ts │ │ ├── ConsumerApi.ts │ │ ├── DestinationApi.ts │ │ ├── GroupApi.ts │ │ ├── InstallationApi.ts │ │ ├── IntegrationApi.ts │ │ ├── OAuthApi.ts │ │ ├── ObjectsFieldsApi.ts │ │ ├── OperationApi.ts │ │ ├── OrgApi.ts │ │ ├── ProjectApi.ts │ │ ├── ProviderApi.ts │ │ ├── ProviderAppApi.ts │ │ ├── RevisionApi.ts │ │ ├── UploadURLApi.ts │ │ ├── UserApi.ts │ │ └── index.ts │ ├── index.ts │ ├── models │ │ ├── AcceptInviteRequest.ts │ │ ├── ApiKey.ts │ │ ├── ApiKeyAsBasicOpts.ts │ │ ├── ApiKeyOpts.ts │ │ ├── ApiKeyOptsHeader.ts │ │ ├── ApiKeyOptsQuery.ts │ │ ├── ApiKeyRequest.ts │ │ ├── ApiProblem.ts │ │ ├── AssociationChangeEvent.ts │ │ ├── AuthHealthCheck.ts │ │ ├── AuthType.ts │ │ ├── Backfill.ts │ │ ├── BackfillConfig.ts │ │ ├── BaseConfigContent.ts │ │ ├── BaseProxyConfig.ts │ │ ├── BaseReadConfig.ts │ │ ├── BaseReadConfigObject.ts │ │ ├── BaseSubscribeConfig.ts │ │ ├── BaseSubscribeConfigObject.ts │ │ ├── BaseWriteConfig.ts │ │ ├── BaseWriteConfigObject.ts │ │ ├── BasicAuthOpts.ts │ │ ├── BatchUpsertIntegrationsRequest.ts │ │ ├── BillingAccount.ts │ │ ├── Builder.ts │ │ ├── BuilderInfo.ts │ │ ├── BuilderInfoOrgRole.ts │ │ ├── BuilderInfoProjectRolesValue.ts │ │ ├── BulkWriteSupport.ts │ │ ├── Config.ts │ │ ├── ConfigContent.ts │ │ ├── ConfigContentAllOf.ts │ │ ├── ConfigCreateEvent.ts │ │ ├── ConfigDeleteEvent.ts │ │ ├── ConfigUpdateEvent.ts │ │ ├── Connection.ts │ │ ├── ConnectionRequest.ts │ │ ├── ConnectionRequestBasicAuth.ts │ │ ├── ConnectionRequestOauth2ClientCredentials.ts │ │ ├── ConnectionRequestOauth2PasswordCredentials.ts │ │ ├── Consumer.ts │ │ ├── CreateBillingAccountSession200Response.ts │ │ ├── CreateBillingAccountSessionRequest.ts │ │ ├── CreateConsumerRequest.ts │ │ ├── CreateDestinationRequest.ts │ │ ├── CreateDestinationRequestMetadata.ts │ │ ├── CreateEvent.ts │ │ ├── CreateGroupRequest.ts │ │ ├── CreateInstallationRequest.ts │ │ ├── CreateInstallationRequestConfig.ts │ │ ├── CreateIntegrationRequest.ts │ │ ├── CreateIntegrationRequestLatestRevision.ts │ │ ├── CreateOrgInviteRequest.ts │ │ ├── CreateOrgRequest.ts │ │ ├── CreateProjectRequest.ts │ │ ├── CreateProviderAppRequest.ts │ │ ├── CreateRevisionRequest.ts │ │ ├── DefaultPeriod.ts │ │ ├── DefaultPeriodConfig.ts │ │ ├── DeleteEvent.ts │ │ ├── Delivery.ts │ │ ├── Destination.ts │ │ ├── FieldMetadata.ts │ │ ├── FieldSetting.ts │ │ ├── FieldSettingDefault.ts │ │ ├── FieldValue.ts │ │ ├── GenerateConnectionRequest.ts │ │ ├── Group.ts │ │ ├── HydratedIntegration.ts │ │ ├── HydratedIntegrationField.ts │ │ ├── HydratedIntegrationFieldExistent.ts │ │ ├── HydratedIntegrationObject.ts │ │ ├── HydratedIntegrationProxy.ts │ │ ├── HydratedIntegrationRead.ts │ │ ├── HydratedIntegrationWrite.ts │ │ ├── HydratedIntegrationWriteObject.ts │ │ ├── HydratedRevision.ts │ │ ├── InputValidationIssue.ts │ │ ├── InputValidationProblem.ts │ │ ├── Installation.ts │ │ ├── Integration.ts │ │ ├── Integration1.ts │ │ ├── IntegrationField.ts │ │ ├── IntegrationFieldExistent.ts │ │ ├── IntegrationFieldMapping.ts │ │ ├── IntegrationObject.ts │ │ ├── IntegrationProxy.ts │ │ ├── IntegrationRead.ts │ │ ├── IntegrationSubscribe.ts │ │ ├── IntegrationSubscribeObject.ts │ │ ├── IntegrationWrite.ts │ │ ├── IntegrationWriteObject.ts │ │ ├── Invite.ts │ │ ├── ListOperations200Response.ts │ │ ├── Log.ts │ │ ├── LogMessage.ts │ │ ├── Media.ts │ │ ├── MediaTypeDarkMode.ts │ │ ├── MediaTypeRegular.ts │ │ ├── MetadataItemInput.ts │ │ ├── MetadataItemPostAuthentication.ts │ │ ├── ModuleInfo.ts │ │ ├── Oauth2AuthorizationCode.ts │ │ ├── Oauth2AuthorizationCodeAccessToken.ts │ │ ├── Oauth2AuthorizationCodeRefreshToken.ts │ │ ├── Oauth2AuthorizationCodeTokensOnly.ts │ │ ├── Oauth2AuthorizationCodeTokensOnlyAccessToken.ts │ │ ├── Oauth2AuthorizationCodeTokensOnlyRefreshToken.ts │ │ ├── Oauth2Opts.ts │ │ ├── OauthConnectRequest.ts │ │ ├── ObjectMetadata.ts │ │ ├── Operation.ts │ │ ├── OptionalFieldsAutoOption.ts │ │ ├── Org.ts │ │ ├── PaginationInfo.ts │ │ ├── PatchApiKeyRequest.ts │ │ ├── PatchApiKeyRequestApiKey.ts │ │ ├── Problem.ts │ │ ├── Project.ts │ │ ├── ProjectEntitlements.ts │ │ ├── ProjectEntitlementsBrandingRemoval.ts │ │ ├── ProviderApp.ts │ │ ├── ProviderInfo.ts │ │ ├── ProviderMetadata1.ts │ │ ├── ProviderMetadataInfo.ts │ │ ├── ReadConfig.ts │ │ ├── ReadConfigAllOf.ts │ │ ├── ReadConfigObject.ts │ │ ├── Revision.ts │ │ ├── SelectedFieldsAutoConfig.ts │ │ ├── SignedUrl.ts │ │ ├── SubscribeConfig.ts │ │ ├── SubscribeConfigAllOf.ts │ │ ├── SubscribeConfigObject.ts │ │ ├── SubscribeOpts.ts │ │ ├── SubscribeSupport.ts │ │ ├── Support.ts │ │ ├── TokenMetadataFields.ts │ │ ├── TokenMetadataFieldsOtherFieldsInner.ts │ │ ├── UpdateConnectionRequest.ts │ │ ├── UpdateDestinationRequest.ts │ │ ├── UpdateDestinationRequestDestination.ts │ │ ├── UpdateDestinationRequestDestinationMetadata.ts │ │ ├── UpdateEvent.ts │ │ ├── UpdateInstallationConfigContent.ts │ │ ├── UpdateInstallationRequest.ts │ │ ├── UpdateInstallationRequestInstallation.ts │ │ ├── UpdateInstallationRequestInstallationConfig.ts │ │ ├── UpdateOrgRequest.ts │ │ ├── UpdateOrgRequestOrg.ts │ │ ├── UpdateProjectRequest.ts │ │ ├── UpdateProjectRequestProject.ts │ │ ├── UpdateProviderAppRequest.ts │ │ ├── UpdateProviderAppRequestProviderApp.ts │ │ ├── ValueDefault.ts │ │ ├── ValueDefaultBoolean.ts │ │ ├── ValueDefaultInteger.ts │ │ ├── ValueDefaultString.ts │ │ ├── ValueDefaults.ts │ │ ├── WriteConfig.ts │ │ ├── WriteConfigAllOf.ts │ │ ├── WriteConfigObject.ts │ │ └── index.ts │ └── runtime.ts │ ├── tsconfig.esm.json │ └── tsconfig.json ├── openapitools.json ├── package.json ├── scripts └── fix-default-field-setting.ts ├── src ├── assets │ ├── ErrorIcon.tsx │ ├── NavIcon.tsx │ ├── PasswordEyeIcon.tsx │ ├── PasswordEyeSlashIcon.tsx │ ├── SettingGearIcon.tsx │ ├── SuccessIcon.tsx │ ├── TooltipIcon.tsx │ └── TrashIcon.tsx ├── components │ ├── Configure │ │ ├── ComponentContainer.tsx │ │ ├── InstallIntegration.tsx │ │ ├── actions │ │ │ ├── mutateAndSetState │ │ │ │ ├── createInstallationAndSetState.ts │ │ │ │ └── updateInstallationAndSetState.ts │ │ │ ├── proxy │ │ │ │ └── isProxyEnabled.ts │ │ │ ├── read │ │ │ │ ├── onSaveReadCreateInstallation.ts │ │ │ │ └── onSaveReadUpdateInstallation.ts │ │ │ └── write │ │ │ │ ├── generateConfigWriteObjects.ts │ │ │ │ ├── onSaveWriteCreateInstallation.ts │ │ │ │ └── onSaveWriteUpdateInstallation.ts │ │ ├── content │ │ │ ├── ConfigureInstallationBase.tsx │ │ │ ├── CreateInstallation.tsx │ │ │ ├── InstallationContent.tsx │ │ │ ├── README.md │ │ │ ├── UpdateInstallation.tsx │ │ │ ├── fields │ │ │ │ ├── FieldDefaultValueMapping │ │ │ │ │ ├── FieldDefaultValueMapping.tsx │ │ │ │ │ ├── FieldDefaultValueTable.tsx │ │ │ │ │ ├── NewDefaultValueUI.tsx │ │ │ │ │ └── setValueDefaultWriteField.tsx │ │ │ │ ├── FieldHeader.tsx │ │ │ │ ├── FieldMappings │ │ │ │ │ ├── DynamicFieldMappings.tsx │ │ │ │ │ ├── FieldMappingRow.tsx │ │ │ │ │ ├── OptionalFieldMappings.tsx │ │ │ │ │ ├── RequiredFieldMappings.tsx │ │ │ │ │ ├── checkDuplicateFieldError.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── setFieldMapping.ts │ │ │ │ │ └── useClearOldFieldMappings.tsx │ │ │ │ ├── ObjectMapping.tsx │ │ │ │ ├── OptionalFields │ │ │ │ │ ├── OptionalFields.tsx │ │ │ │ │ ├── OptionalFieldsV2.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── optionalFields.module.css │ │ │ │ │ └── setOptionalField.ts │ │ │ │ ├── ReadFields.tsx │ │ │ │ ├── RequiredFields.tsx │ │ │ │ ├── ValueMapping │ │ │ │ │ ├── ValueHeader.tsx │ │ │ │ │ ├── ValueMappingItem.tsx │ │ │ │ │ ├── ValuesMapping.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── setValueMapping.ts │ │ │ │ └── WriteFields │ │ │ │ │ ├── WriteFields.tsx │ │ │ │ │ ├── WriteFieldsV2.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── setNonConfigurableWriteField.tsx │ │ │ ├── manage │ │ │ │ ├── AuthenticationSection.tsx │ │ │ │ ├── ManageContent.tsx │ │ │ │ ├── UninstallSection.tsx │ │ │ │ ├── authenticate.module.css │ │ │ │ └── updateConnection │ │ │ │ │ ├── UpdateApiKeyConnect.tsx │ │ │ │ │ ├── UpdateBasicAuthConnect.tsx │ │ │ │ │ ├── UpdateClientCredentialsConnect.tsx │ │ │ │ │ ├── UpdateConnectionSection.tsx │ │ │ │ │ └── UpdateOauthConnect.tsx │ │ │ ├── useMutateInstallation.tsx │ │ │ └── useSelectedConfigureState.tsx │ │ ├── index.ts │ │ ├── layout │ │ │ ├── ConditionalHasConfigurationLayout │ │ │ │ ├── ConditionalHasConfigurationLayout.tsx │ │ │ │ └── InstalledSuccessBox.tsx │ │ │ ├── ProtectedConnectionLayout.tsx │ │ │ └── UninstallButton.tsx │ │ ├── nav │ │ │ └── ObjectManagementNav │ │ │ │ ├── NavObjectItemProps.tsx │ │ │ │ ├── ObjectManagementNavContext.tsx │ │ │ │ ├── constant.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── v2 │ │ │ │ ├── Tabs │ │ │ │ ├── ManageTab.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── tabs.module.css │ │ │ │ └── index.tsx │ │ ├── state │ │ │ ├── ConfigurationStateProvider.tsx │ │ │ ├── HydratedRevisionContext.tsx │ │ │ └── utils.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── Connect │ │ ├── ConnectProvider.tsx │ │ ├── ConnectedSuccessBox.tsx │ │ ├── ManageConnectionSection.tsx │ │ ├── RemoveConnectionButton.tsx │ │ ├── RemoveConnectionSection.tsx │ │ └── useConnectionHandler.tsx │ ├── Docs │ │ └── DocsHelperText.tsx │ ├── ErrorTextBox │ │ ├── ErrorTextBox.tsx │ │ └── errorTextBox.module.css │ ├── FormCalloutBox.tsx │ ├── FormErrorBox.tsx │ ├── FormSuccessBox.tsx │ ├── Loading │ │ ├── index.tsx │ │ └── style.module.css │ ├── RedirectHandler │ │ ├── RedirectHandler.tsx │ │ ├── RedirectLoading.tsx │ │ └── index.ts │ ├── SuccessTextBox │ │ └── SuccessTextBox.tsx │ ├── auth │ │ ├── ApiKeyAuth │ │ │ ├── ApiKeyAuthContent.tsx │ │ │ ├── ApiKeyAuthFlow.tsx │ │ │ └── LandingContentProps.tsx │ │ ├── AuthCardLayoutTemplate.tsx │ │ ├── AuthErrorAlert │ │ │ └── AuthErrorAlert.tsx │ │ ├── BasicAuth │ │ │ ├── BasicAuthContent.tsx │ │ │ ├── BasicAuthFlow.tsx │ │ │ ├── BasicAuthFlowProps.tsx │ │ │ └── LandingContentProps.tsx │ │ ├── NoAuth │ │ │ ├── LandingContentProps.tsx │ │ │ ├── NoAuthContent.tsx │ │ │ └── NoAuthFlow.tsx │ │ ├── Oauth │ │ │ ├── AuthorizationCode │ │ │ │ ├── NoWorkspaceEntry │ │ │ │ │ ├── LandingContentProps.tsx │ │ │ │ │ ├── NoWorkspaceEntryContent.tsx │ │ │ │ │ └── NoWorkspaceOauthFlow.tsx │ │ │ │ ├── NoWorkspaceOauthFlow2 │ │ │ │ │ └── NoWorkspaceOauthFlow2.tsx │ │ │ │ ├── OAuthWindow │ │ │ │ │ ├── OAuthWindow.tsx │ │ │ │ │ └── windowHelpers.tsx │ │ │ │ ├── OauthFlow2 │ │ │ │ │ └── OauthFlow2.tsx │ │ │ │ ├── WorkspaceEntry │ │ │ │ │ ├── Salesforce │ │ │ │ │ │ ├── SalesforceSubdomainEntry.tsx │ │ │ │ │ │ └── SubdomainEntryProps.tsx │ │ │ │ │ ├── WorkspaceEntryContent.tsx │ │ │ │ │ ├── WorkspaceEntryProps.tsx │ │ │ │ │ └── WorkspaceOauthFlow.tsx │ │ │ │ ├── enableCSRFprotection.ts │ │ │ │ └── useOAuthPopupURL.ts │ │ │ ├── ClientCredentials │ │ │ │ ├── ClientCredentials.tsx │ │ │ │ ├── ClientCredentialsContainer.tsx │ │ │ │ ├── ClientCredentialsContent.tsx │ │ │ │ └── ClientCredentialsCredsContent.tsx │ │ │ └── OauthFlow │ │ │ │ └── OauthFlow.tsx │ │ ├── providerMetadata.ts │ │ └── useCreateConnectionMutation.ts │ ├── form │ │ ├── FormControl │ │ │ ├── formControl.module.css │ │ │ └── index.tsx │ │ ├── Input │ │ │ ├── index.tsx │ │ │ └── input.module.css │ │ ├── PasswordInput │ │ │ └── index.tsx │ │ ├── Textarea │ │ │ ├── index.tsx │ │ │ └── textarea.module.css │ │ └── index.ts │ └── ui-base │ │ ├── AccessibleLink │ │ ├── index.tsx │ │ └── link.module.css │ │ ├── Box │ │ ├── Box.tsx │ │ └── box.module.css │ │ ├── Button │ │ ├── button.module.css │ │ └── index.tsx │ │ ├── Checkbox │ │ ├── checkbox.module.css │ │ └── index.tsx │ │ ├── ComboBox │ │ ├── ComboBox.tsx │ │ └── combobox.module.css │ │ ├── Container │ │ ├── Container.tsx │ │ └── container.module.css │ │ ├── Divider │ │ ├── divider.module.css │ │ └── index.tsx │ │ ├── Tag │ │ └── index.tsx │ │ └── Tooltip │ │ └── index.tsx ├── context │ ├── AmpersandContextProvider │ │ ├── AmpersandContextProvider.tsx │ │ └── index.ts │ ├── ApiKeyContextProvider.tsx │ ├── ConnectionsContextProvider.tsx │ ├── ErrorContextProvider.tsx │ ├── InstallIIntegrationContextProvider │ │ ├── InstallIntegrationContextProvider.tsx │ │ └── useIsInstallationDeleted.ts │ ├── IntegrationListContextProvider.tsx │ └── ProjectContextProvider.tsx ├── global.d.ts ├── headless │ ├── InstallationProvider.tsx │ ├── config │ │ ├── ConfigContext.tsx │ │ ├── types.ts │ │ └── useConfigHelper.tsx │ ├── index.ts │ ├── installation │ │ ├── useCreateInstallation.ts │ │ ├── useDeleteInstallation.ts │ │ ├── useInstallation.ts │ │ └── useUpdateInstallation.ts │ ├── manifest │ │ ├── useHydratedRevisionQuery.ts │ │ └── useManifest.ts │ ├── types.ts │ └── useConnection.ts ├── hooks │ ├── index.ts │ ├── mutation │ │ ├── useCreateInstallationMutation.ts │ │ ├── useCreateOauthConnectionMutation.ts │ │ ├── useDeleteConnectionMutation.ts │ │ ├── useDeleteInstallationMutation.ts │ │ ├── useUpdateConnectionMutation.ts │ │ ├── useUpdateInstallationMutation.ts │ │ └── useUpdateOauthConnectMutation.ts │ ├── query │ │ ├── index.ts │ │ ├── useConnectionQuery.ts │ │ ├── useConnectionsListQuery.ts │ │ ├── useIntegrationListQuery.ts │ │ ├── useIntegrationQuery.ts │ │ ├── useListInstallationsQuery.ts │ │ ├── useListProviderAppsQuery.ts │ │ ├── useOauthConnectQuery.ts │ │ └── useProjectWithEntitlementsQuery.ts │ ├── useForceUpdate.tsx │ ├── useIsIntegrationInstalled.ts │ └── useProvider.tsx ├── index.ts ├── layout │ └── AuthCardLayout │ │ ├── AmpersandFooter.tsx │ │ └── AuthCardLayout.tsx ├── public │ └── index.ts ├── services │ ├── ApiService.ts │ ├── api.ts │ └── version.ts ├── styles │ ├── resetCss.module.css │ └── variables.css └── utils │ ├── handleServerError.ts │ └── index.ts ├── tsconfig.json ├── vite.config.ts └── yarn.lock /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | ignore: 8 | - dependency-name: "@openapitools/openapi-generator-cli" 9 | -------------------------------------------------------------------------------- /.github/workflows/codesee-arch-diagram.yml: -------------------------------------------------------------------------------- 1 | # This workflow was added by CodeSee. Learn more at https://codesee.io/ 2 | # This is v2.0 of this workflow file 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request_target: 8 | types: [opened, synchronize, reopened] 9 | 10 | name: CodeSee 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | codesee: 16 | runs-on: ubuntu-latest 17 | continue-on-error: true 18 | name: Analyze the repo with CodeSee 19 | steps: 20 | - uses: Codesee-io/codesee-action@v2 21 | with: 22 | codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} 23 | codesee-url: https://app.codesee.io 24 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install modules 13 | run: yarn 14 | - name: Run linter 15 | run: yarn lint:dry 16 | - name: Build project 17 | run: | 18 | set -e # Exit immediately if a command exits with a non-zero status 19 | yarn build 20 | # This ensures that if the build fails, the GitHub Action will also fail -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish React SDK to npm 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version number for the SDK' 8 | required: true 9 | tag: 10 | description: 'Tag for the SDK version' 11 | required: true 12 | default: 'latest' 13 | 14 | jobs: 15 | publish: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | token: ${{ secrets.AMPERSAND_OPS_PAT }} 23 | ref: main 24 | 25 | - name: Set up Node.js 26 | uses: actions/setup-node@v3 27 | with: 28 | node-version: '18' 29 | cache: 'npm' 30 | always-auth: true 31 | 32 | - name: Install yarn 33 | run: npm i -g yarn 34 | 35 | - name: Install modules 36 | run: yarn 37 | 38 | - name: Update version number 39 | run: yarn version --new-version ${{ github.event.inputs.version }} --no-git-tag-version 40 | 41 | - name: Build project 42 | run: yarn build 43 | 44 | - name: Publish to npm 45 | id: publish_to_npm 46 | run: echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc && yarn publish --non-interactive --tag ${{ github.event.inputs.tag }} 47 | env: 48 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | 50 | - name: Push changes back to repository 51 | if: steps.publish_to_npm.conclusion == 'success' 52 | run: | 53 | git config --global user.email "devops@withampersand.com" 54 | git config --global user.name "Ampersand Ops" 55 | git add package.json src/services/version.ts 56 | git commit -m "[ampersand-ops] release: version ${{ github.event.inputs.version }}" 57 | git tag v${{ github.event.inputs.version }} 58 | git remote set-url origin https://x-access-token:${{ secrets.AMPERSAND_OPS_PAT }}@github.com/${{ github.repository }} 59 | git push origin --tags 60 | git push origin HEAD:${{ github.ref }} 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.AMPERSAND_OPS_PAT }} 63 | 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependency directories 2 | node_modules/ 3 | 4 | # output files 5 | build/ 6 | 7 | # npm cache directory 8 | .npm 9 | 10 | # MacOS 11 | **/.DS_Store 12 | 13 | # generated tar files 14 | .tgz 15 | 16 | src/services/version.ts 17 | 18 | .idea/ 19 | 20 | # bundle analyzer report 21 | stats.html 22 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # This ensures that when "npm install" is run in this directory, peer dependencies are not installed. 2 | # This prevents the installation of react, so that when this package is used with a local test app which depends 3 | # this directory via a symlink, there won't be 2 instances of React running, otherwise the test app will error. 4 | # See https://docs.npmjs.com/cli/v9/using-npm/config#legacy-peer-deps. 5 | legacy-peer-deps = true 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // .vscode/settings.json 2 | { 3 | "eslint.format.enable": true, 4 | "eslint.useFlatConfig": true, 5 | 6 | // run ESLint’s fix-all when you hit ⌘S (manual save) … 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": "explicit" // or simply true 9 | }, 10 | 11 | // …and make ESLint the formatter that runs for Shift+Option+F 12 | "[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, 13 | "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, 14 | "[javascriptreact]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, 15 | "[typescriptreact]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" } 16 | } 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Scripts 2 | 3 | To build this repo for development with hot reload, run: 4 | ```sh 5 | npm run watch 6 | ``` 7 | 8 | To build this repo for production, run: 9 | ```sh 10 | npm run build 11 | ``` 12 | 13 | To run the test suite, run: 14 | ```sh 15 | npm run test 16 | ``` 17 | 18 | ## Linting 19 | 20 | To run the linter, run: 21 | ```sh 22 | npm run lint 23 | ``` 24 | 25 | To integrate VSCode with this repo's `eslint` settings, please install the "ESLint" Extension. The rules in the `.vscode/settings.json` file of this repo defines the behavior of the extension on this workspace. 26 | 27 | ## SDK generation 28 | 29 | ### Repo Setup 30 | To generate the SDK from the openapi spec in the server repo, follow these steps: 31 | 32 | #### Install Java 33 | java is required for sdk generation 34 | 35 | To install java on Mac 36 | ``` 37 | brew install java 38 | ``` 39 | 40 | Then check your installation by running 41 | 42 | ``` 43 | java -version 44 | ``` 45 | 46 | If you see this: 47 | 48 | ``` 49 | The operation couldn’t be completed. Unable to locate a Java Runtime. 50 | Please visit http://www.java.com for information on installing Java. 51 | ``` 52 | 53 | Then you need to create a symlink for the system Java wrappers to find this JDK: 54 | 55 | ``` 56 | sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk \ 57 | /Library/Java/JavaVirtualMachines/openjdk.jdk 58 | ``` 59 | 60 | More info at [https://stackoverflow.com/a/65601197](https://stackoverflow.com/a/65601197) 61 | 62 | #### Clone server repo 63 | We need to clone the [server repo](https://github.com/amp-labs/server) as a sibling directory, meaning that it lives in the same nesting level as this repo, here is an example directory structure: 64 | ``` 65 | - amp-labs 66 | -- server 67 | -- react 68 | ``` 69 | 70 | #### Generate the SDK 71 | cd into `react` directory and run the following: 72 | 73 | ```yarn generate-api``` 74 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This is a builder image for the project. Feel free to use it, but it's 2 | # not an actual working environment meant for production, it's for releases 3 | # and certain automations. 4 | FROM node:22-alpine3.19 5 | SHELL ["/bin/ash", "-o", "pipefail", "-c"] 6 | WORKDIR /app 7 | # Install dependencies 8 | RUN apk update && apk add --no-cache ca-certificates git bash openjdk11-jre jq openssh python3 && rm -rf /var/cache/apk/* 9 | COPY . . 10 | # Pre-cache node modules, set up ssh, install gh, and install gcloud 11 | RUN yarn install && \ 12 | mv node_modules /tmp/ && \ 13 | cd / && \ 14 | rm -rf /app && \ 15 | mkdir /app && \ 16 | mv /tmp/node_modules /app/ && \ 17 | cd /app && \ 18 | mkdir -p /root/.ssh && \ 19 | chmod 700 /root/.ssh && \ 20 | ssh-keyscan -t rsa github.com >> /root/.ssh/known_hosts && \ 21 | wget https://github.com/cli/cli/releases/download/v2.54.0/gh_2.54.0_linux_amd64.tar.gz && \ 22 | tar -xvf gh_2.54.0_linux_amd64.tar.gz && \ 23 | cd gh_2.54.0_linux_amd64 && \ 24 | mv bin/gh /usr/local/bin/ && \ 25 | cd /app && \ 26 | rm -rf gh_2.54.0_linux_amd64 && \ 27 | rm gh_2.54.0_linux_amd64.tar.gz && \ 28 | chmod +x /usr/local/bin/gh && \ 29 | wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz && \ 30 | tar -xvf google-cloud-cli-linux-x86_64.tar.gz && \ 31 | rm -f google-cloud-cli-linux-x86_64.tar.gz && \ 32 | mv google-cloud-sdk /usr/local/ && \ 33 | /usr/local/google-cloud-sdk/install.sh --quiet 34 | 35 | ENV PATH="/usr/local/google-cloud-sdk/bin:${PATH}" 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Onset Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', {targets: {node: 'current'}}], 4 | '@babel/preset-typescript', 5 | ], 6 | }; -------------------------------------------------------------------------------- /generated-sources/api/.gitignore: -------------------------------------------------------------------------------- 1 | wwwroot/*.js 2 | node_modules 3 | typings 4 | dist 5 | -------------------------------------------------------------------------------- /generated-sources/api/.npmignore: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /generated-sources/api/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /generated-sources/api/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 6.6.0 -------------------------------------------------------------------------------- /generated-sources/api/README.md: -------------------------------------------------------------------------------- 1 | ## amp-labs-generated-rest-sdk@1.0.0 2 | 3 | This generator creates TypeScript/JavaScript client that utilizes [Fetch API](https://fetch.spec.whatwg.org/). The generated Node module can be used in the following environments: 4 | 5 | Environment 6 | * Node.js 7 | * Webpack 8 | * Browserify 9 | 10 | Language level 11 | * ES5 - you must have a Promises/A+ library installed 12 | * ES6 13 | 14 | Module system 15 | * CommonJS 16 | * ES6 module system 17 | 18 | It can be used in both TypeScript and JavaScript. In TypeScript, the definition should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html)) 19 | 20 | ### Building 21 | 22 | To build and compile the typescript sources to javascript use: 23 | ``` 24 | npm install 25 | npm run build 26 | ``` 27 | 28 | ### Publishing 29 | 30 | First build the package then run ```npm publish``` 31 | 32 | ### Consuming 33 | 34 | navigate to the folder of your consuming project and run one of the following commands. 35 | 36 | _published:_ 37 | 38 | ``` 39 | npm install amp-labs-generated-rest-sdk@1.0.0 --save 40 | ``` 41 | 42 | _unPublished (not recommended):_ 43 | 44 | ``` 45 | npm install PATH_TO_GENERATED_PACKAGE --save 46 | -------------------------------------------------------------------------------- /generated-sources/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amp-labs-generated-rest-sdk", 3 | "version": "1.0.0", 4 | "description": "OpenAPI client for amp-labs-generated-rest-sdk", 5 | "author": "OpenAPI-Generator", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/GIT_USER_ID/GIT_REPO_ID.git" 9 | }, 10 | "main": "./dist/index.js", 11 | "typings": "./dist/index.d.ts", 12 | "module": "./dist/esm/index.js", 13 | "sideEffects": false, 14 | "scripts": { 15 | "build": "tsc && tsc -p tsconfig.esm.json", 16 | "prepare": "npm run build" 17 | }, 18 | "devDependencies": { 19 | "typescript": "^4.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /generated-sources/api/src/apis/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export * from './APIKeyApi'; 4 | export * from './BillingAccountApi'; 5 | export * from './ConnectionApi'; 6 | export * from './ConsumerApi'; 7 | export * from './DestinationApi'; 8 | export * from './GroupApi'; 9 | export * from './InstallationApi'; 10 | export * from './IntegrationApi'; 11 | export * from './OAuthApi'; 12 | export * from './ObjectsFieldsApi'; 13 | export * from './OperationApi'; 14 | export * from './OrgApi'; 15 | export * from './ProjectApi'; 16 | export * from './ProviderApi'; 17 | export * from './ProviderAppApi'; 18 | export * from './RevisionApi'; 19 | export * from './UploadURLApi'; 20 | export * from './UserApi'; 21 | -------------------------------------------------------------------------------- /generated-sources/api/src/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export * from './runtime'; 4 | export * from './apis'; 5 | export * from './models'; 6 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/AcceptInviteRequest.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface AcceptInviteRequest 20 | */ 21 | export interface AcceptInviteRequest { 22 | /** 23 | * The email address that the invite was sent to 24 | * @type {string} 25 | * @memberof AcceptInviteRequest 26 | */ 27 | invitedEmail: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the AcceptInviteRequest interface. 32 | */ 33 | export function instanceOfAcceptInviteRequest(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "invitedEmail" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function AcceptInviteRequestFromJSON(json: any): AcceptInviteRequest { 41 | return AcceptInviteRequestFromJSONTyped(json, false); 42 | } 43 | 44 | export function AcceptInviteRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): AcceptInviteRequest { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'invitedEmail': json['invitedEmail'], 51 | }; 52 | } 53 | 54 | export function AcceptInviteRequestToJSON(value?: AcceptInviteRequest | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'invitedEmail': value.invitedEmail, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/ApiKeyOptsQuery.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * Configuration for API key in query parameter. Must be provided if type is in-query. 18 | * @export 19 | * @interface ApiKeyOptsQuery 20 | */ 21 | export interface ApiKeyOptsQuery { 22 | /** 23 | * The name of the query parameter to be used for the API key. 24 | * @type {string} 25 | * @memberof ApiKeyOptsQuery 26 | */ 27 | name: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ApiKeyOptsQuery interface. 32 | */ 33 | export function instanceOfApiKeyOptsQuery(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "name" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function ApiKeyOptsQueryFromJSON(json: any): ApiKeyOptsQuery { 41 | return ApiKeyOptsQueryFromJSONTyped(json, false); 42 | } 43 | 44 | export function ApiKeyOptsQueryFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiKeyOptsQuery { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'name': json['name'], 51 | }; 52 | } 53 | 54 | export function ApiKeyOptsQueryToJSON(value?: ApiKeyOptsQuery | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'name': value.name, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/ApiKeyRequest.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface ApiKeyRequest 20 | */ 21 | export interface ApiKeyRequest { 22 | /** 23 | * A short name for the API key. 24 | * @type {string} 25 | * @memberof ApiKeyRequest 26 | */ 27 | label: string; 28 | /** 29 | * The scopes for the API key. 30 | * @type {Array} 31 | * @memberof ApiKeyRequest 32 | */ 33 | scopes?: Array; 34 | } 35 | 36 | /** 37 | * Check if a given object implements the ApiKeyRequest interface. 38 | */ 39 | export function instanceOfApiKeyRequest(value: object): boolean { 40 | let isInstance = true; 41 | isInstance = isInstance && "label" in value; 42 | 43 | return isInstance; 44 | } 45 | 46 | export function ApiKeyRequestFromJSON(json: any): ApiKeyRequest { 47 | return ApiKeyRequestFromJSONTyped(json, false); 48 | } 49 | 50 | export function ApiKeyRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiKeyRequest { 51 | if ((json === undefined) || (json === null)) { 52 | return json; 53 | } 54 | return { 55 | 56 | 'label': json['label'], 57 | 'scopes': !exists(json, 'scopes') ? undefined : json['scopes'], 58 | }; 59 | } 60 | 61 | export function ApiKeyRequestToJSON(value?: ApiKeyRequest | null): any { 62 | if (value === undefined) { 63 | return undefined; 64 | } 65 | if (value === null) { 66 | return null; 67 | } 68 | return { 69 | 70 | 'label': value.label, 71 | 'scopes': value.scopes, 72 | }; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/AuthType.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | /** 17 | * The type of authentication required by the provider. 18 | * @export 19 | */ 20 | export const AuthType = { 21 | Oauth2: 'oauth2', 22 | ApiKey: 'apiKey', 23 | Basic: 'basic', 24 | Jwt: 'jwt', 25 | None: 'none' 26 | } as const; 27 | export type AuthType = typeof AuthType[keyof typeof AuthType]; 28 | 29 | 30 | export function AuthTypeFromJSON(json: any): AuthType { 31 | return AuthTypeFromJSONTyped(json, false); 32 | } 33 | 34 | export function AuthTypeFromJSONTyped(json: any, ignoreDiscriminator: boolean): AuthType { 35 | return json as AuthType; 36 | } 37 | 38 | export function AuthTypeToJSON(value?: AuthType | null): any { 39 | return value as any; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/Backfill.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | import type { DefaultPeriod } from './DefaultPeriod'; 17 | import { 18 | DefaultPeriodFromJSON, 19 | DefaultPeriodFromJSONTyped, 20 | DefaultPeriodToJSON, 21 | } from './DefaultPeriod'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface Backfill 27 | */ 28 | export interface Backfill { 29 | /** 30 | * 31 | * @type {DefaultPeriod} 32 | * @memberof Backfill 33 | */ 34 | defaultPeriod: DefaultPeriod; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the Backfill interface. 39 | */ 40 | export function instanceOfBackfill(value: object): boolean { 41 | let isInstance = true; 42 | isInstance = isInstance && "defaultPeriod" in value; 43 | 44 | return isInstance; 45 | } 46 | 47 | export function BackfillFromJSON(json: any): Backfill { 48 | return BackfillFromJSONTyped(json, false); 49 | } 50 | 51 | export function BackfillFromJSONTyped(json: any, ignoreDiscriminator: boolean): Backfill { 52 | if ((json === undefined) || (json === null)) { 53 | return json; 54 | } 55 | return { 56 | 57 | 'defaultPeriod': DefaultPeriodFromJSON(json['defaultPeriod']), 58 | }; 59 | } 60 | 61 | export function BackfillToJSON(value?: Backfill | null): any { 62 | if (value === undefined) { 63 | return undefined; 64 | } 65 | if (value === null) { 66 | return null; 67 | } 68 | return { 69 | 70 | 'defaultPeriod': DefaultPeriodToJSON(value.defaultPeriod), 71 | }; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/BackfillConfig.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | import type { DefaultPeriodConfig } from './DefaultPeriodConfig'; 17 | import { 18 | DefaultPeriodConfigFromJSON, 19 | DefaultPeriodConfigFromJSONTyped, 20 | DefaultPeriodConfigToJSON, 21 | } from './DefaultPeriodConfig'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface BackfillConfig 27 | */ 28 | export interface BackfillConfig { 29 | /** 30 | * 31 | * @type {DefaultPeriodConfig} 32 | * @memberof BackfillConfig 33 | */ 34 | defaultPeriod: DefaultPeriodConfig; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the BackfillConfig interface. 39 | */ 40 | export function instanceOfBackfillConfig(value: object): boolean { 41 | let isInstance = true; 42 | isInstance = isInstance && "defaultPeriod" in value; 43 | 44 | return isInstance; 45 | } 46 | 47 | export function BackfillConfigFromJSON(json: any): BackfillConfig { 48 | return BackfillConfigFromJSONTyped(json, false); 49 | } 50 | 51 | export function BackfillConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): BackfillConfig { 52 | if ((json === undefined) || (json === null)) { 53 | return json; 54 | } 55 | return { 56 | 57 | 'defaultPeriod': DefaultPeriodConfigFromJSON(json['defaultPeriod']), 58 | }; 59 | } 60 | 61 | export function BackfillConfigToJSON(value?: BackfillConfig | null): any { 62 | if (value === undefined) { 63 | return undefined; 64 | } 65 | if (value === null) { 66 | return null; 67 | } 68 | return { 69 | 70 | 'defaultPeriod': DefaultPeriodConfigToJSON(value.defaultPeriod), 71 | }; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/BaseProxyConfig.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface BaseProxyConfig 20 | */ 21 | export interface BaseProxyConfig { 22 | /** 23 | * 24 | * @type {boolean} 25 | * @memberof BaseProxyConfig 26 | */ 27 | enabled?: boolean; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the BaseProxyConfig interface. 32 | */ 33 | export function instanceOfBaseProxyConfig(value: object): boolean { 34 | let isInstance = true; 35 | 36 | return isInstance; 37 | } 38 | 39 | export function BaseProxyConfigFromJSON(json: any): BaseProxyConfig { 40 | return BaseProxyConfigFromJSONTyped(json, false); 41 | } 42 | 43 | export function BaseProxyConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): BaseProxyConfig { 44 | if ((json === undefined) || (json === null)) { 45 | return json; 46 | } 47 | return { 48 | 49 | 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], 50 | }; 51 | } 52 | 53 | export function BaseProxyConfigToJSON(value?: BaseProxyConfig | null): any { 54 | if (value === undefined) { 55 | return undefined; 56 | } 57 | if (value === null) { 58 | return null; 59 | } 60 | return { 61 | 62 | 'enabled': value.enabled, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/CreateBillingAccountSession200Response.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface CreateBillingAccountSession200Response 20 | */ 21 | export interface CreateBillingAccountSession200Response { 22 | /** 23 | * The URL to redirect to in order to start the portal session. 24 | * @type {string} 25 | * @memberof CreateBillingAccountSession200Response 26 | */ 27 | url: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the CreateBillingAccountSession200Response interface. 32 | */ 33 | export function instanceOfCreateBillingAccountSession200Response(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "url" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function CreateBillingAccountSession200ResponseFromJSON(json: any): CreateBillingAccountSession200Response { 41 | return CreateBillingAccountSession200ResponseFromJSONTyped(json, false); 42 | } 43 | 44 | export function CreateBillingAccountSession200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateBillingAccountSession200Response { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'url': json['url'], 51 | }; 52 | } 53 | 54 | export function CreateBillingAccountSession200ResponseToJSON(value?: CreateBillingAccountSession200Response | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'url': value.url, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/CreateEvent.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface CreateEvent 20 | */ 21 | export interface CreateEvent { 22 | /** 23 | * If always, the integration will subscribe to create events by default. 24 | * @type {string} 25 | * @memberof CreateEvent 26 | */ 27 | enabled?: CreateEventEnabledEnum; 28 | } 29 | 30 | 31 | /** 32 | * @export 33 | */ 34 | export const CreateEventEnabledEnum = { 35 | Always: 'always' 36 | } as const; 37 | export type CreateEventEnabledEnum = typeof CreateEventEnabledEnum[keyof typeof CreateEventEnabledEnum]; 38 | 39 | 40 | /** 41 | * Check if a given object implements the CreateEvent interface. 42 | */ 43 | export function instanceOfCreateEvent(value: object): boolean { 44 | let isInstance = true; 45 | 46 | return isInstance; 47 | } 48 | 49 | export function CreateEventFromJSON(json: any): CreateEvent { 50 | return CreateEventFromJSONTyped(json, false); 51 | } 52 | 53 | export function CreateEventFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateEvent { 54 | if ((json === undefined) || (json === null)) { 55 | return json; 56 | } 57 | return { 58 | 59 | 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], 60 | }; 61 | } 62 | 63 | export function CreateEventToJSON(value?: CreateEvent | null): any { 64 | if (value === undefined) { 65 | return undefined; 66 | } 67 | if (value === null) { 68 | return null; 69 | } 70 | return { 71 | 72 | 'enabled': value.enabled, 73 | }; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/CreateOrgInviteRequest.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface CreateOrgInviteRequest 20 | */ 21 | export interface CreateOrgInviteRequest { 22 | /** 23 | * The email address of the user to invite. 24 | * @type {string} 25 | * @memberof CreateOrgInviteRequest 26 | */ 27 | email: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the CreateOrgInviteRequest interface. 32 | */ 33 | export function instanceOfCreateOrgInviteRequest(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "email" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function CreateOrgInviteRequestFromJSON(json: any): CreateOrgInviteRequest { 41 | return CreateOrgInviteRequestFromJSONTyped(json, false); 42 | } 43 | 44 | export function CreateOrgInviteRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateOrgInviteRequest { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'email': json['email'], 51 | }; 52 | } 53 | 54 | export function CreateOrgInviteRequestToJSON(value?: CreateOrgInviteRequest | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'email': value.email, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/CreateOrgRequest.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface CreateOrgRequest 20 | */ 21 | export interface CreateOrgRequest { 22 | /** 23 | * The organization label. 24 | * @type {string} 25 | * @memberof CreateOrgRequest 26 | */ 27 | label: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the CreateOrgRequest interface. 32 | */ 33 | export function instanceOfCreateOrgRequest(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "label" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function CreateOrgRequestFromJSON(json: any): CreateOrgRequest { 41 | return CreateOrgRequestFromJSONTyped(json, false); 42 | } 43 | 44 | export function CreateOrgRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateOrgRequest { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'label': json['label'], 51 | }; 52 | } 53 | 54 | export function CreateOrgRequestToJSON(value?: CreateOrgRequest | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'label': value.label, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/DeleteEvent.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface DeleteEvent 20 | */ 21 | export interface DeleteEvent { 22 | /** 23 | * If always, the integration will subscribe to delete events by default. 24 | * @type {string} 25 | * @memberof DeleteEvent 26 | */ 27 | enabled?: DeleteEventEnabledEnum; 28 | } 29 | 30 | 31 | /** 32 | * @export 33 | */ 34 | export const DeleteEventEnabledEnum = { 35 | Always: 'always' 36 | } as const; 37 | export type DeleteEventEnabledEnum = typeof DeleteEventEnabledEnum[keyof typeof DeleteEventEnabledEnum]; 38 | 39 | 40 | /** 41 | * Check if a given object implements the DeleteEvent interface. 42 | */ 43 | export function instanceOfDeleteEvent(value: object): boolean { 44 | let isInstance = true; 45 | 46 | return isInstance; 47 | } 48 | 49 | export function DeleteEventFromJSON(json: any): DeleteEvent { 50 | return DeleteEventFromJSONTyped(json, false); 51 | } 52 | 53 | export function DeleteEventFromJSONTyped(json: any, ignoreDiscriminator: boolean): DeleteEvent { 54 | if ((json === undefined) || (json === null)) { 55 | return json; 56 | } 57 | return { 58 | 59 | 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], 60 | }; 61 | } 62 | 63 | export function DeleteEventToJSON(value?: DeleteEvent | null): any { 64 | if (value === undefined) { 65 | return undefined; 66 | } 67 | if (value === null) { 68 | return null; 69 | } 70 | return { 71 | 72 | 'enabled': value.enabled, 73 | }; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/FieldValue.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * Represents a field value 18 | * @export 19 | * @interface FieldValue 20 | */ 21 | export interface FieldValue { 22 | /** 23 | * The internal value used by the system 24 | * @type {string} 25 | * @memberof FieldValue 26 | */ 27 | value: string; 28 | /** 29 | * The human-readable display value 30 | * @type {string} 31 | * @memberof FieldValue 32 | */ 33 | displayValue: string; 34 | } 35 | 36 | /** 37 | * Check if a given object implements the FieldValue interface. 38 | */ 39 | export function instanceOfFieldValue(value: object): boolean { 40 | let isInstance = true; 41 | isInstance = isInstance && "value" in value; 42 | isInstance = isInstance && "displayValue" in value; 43 | 44 | return isInstance; 45 | } 46 | 47 | export function FieldValueFromJSON(json: any): FieldValue { 48 | return FieldValueFromJSONTyped(json, false); 49 | } 50 | 51 | export function FieldValueFromJSONTyped(json: any, ignoreDiscriminator: boolean): FieldValue { 52 | if ((json === undefined) || (json === null)) { 53 | return json; 54 | } 55 | return { 56 | 57 | 'value': json['value'], 58 | 'displayValue': json['displayValue'], 59 | }; 60 | } 61 | 62 | export function FieldValueToJSON(value?: FieldValue | null): any { 63 | if (value === undefined) { 64 | return undefined; 65 | } 66 | if (value === null) { 67 | return null; 68 | } 69 | return { 70 | 71 | 'value': value.value, 72 | 'displayValue': value.displayValue, 73 | }; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/HydratedIntegrationProxy.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface HydratedIntegrationProxy 20 | */ 21 | export interface HydratedIntegrationProxy { 22 | /** 23 | * 24 | * @type {boolean} 25 | * @memberof HydratedIntegrationProxy 26 | */ 27 | enabled?: boolean; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the HydratedIntegrationProxy interface. 32 | */ 33 | export function instanceOfHydratedIntegrationProxy(value: object): boolean { 34 | let isInstance = true; 35 | 36 | return isInstance; 37 | } 38 | 39 | export function HydratedIntegrationProxyFromJSON(json: any): HydratedIntegrationProxy { 40 | return HydratedIntegrationProxyFromJSONTyped(json, false); 41 | } 42 | 43 | export function HydratedIntegrationProxyFromJSONTyped(json: any, ignoreDiscriminator: boolean): HydratedIntegrationProxy { 44 | if ((json === undefined) || (json === null)) { 45 | return json; 46 | } 47 | return { 48 | 49 | 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], 50 | }; 51 | } 52 | 53 | export function HydratedIntegrationProxyToJSON(value?: HydratedIntegrationProxy | null): any { 54 | if (value === undefined) { 55 | return undefined; 56 | } 57 | if (value === null) { 58 | return null; 59 | } 60 | return { 61 | 62 | 'enabled': value.enabled, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/IntegrationProxy.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface IntegrationProxy 20 | */ 21 | export interface IntegrationProxy { 22 | /** 23 | * 24 | * @type {boolean} 25 | * @memberof IntegrationProxy 26 | */ 27 | enabled?: boolean; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the IntegrationProxy interface. 32 | */ 33 | export function instanceOfIntegrationProxy(value: object): boolean { 34 | let isInstance = true; 35 | 36 | return isInstance; 37 | } 38 | 39 | export function IntegrationProxyFromJSON(json: any): IntegrationProxy { 40 | return IntegrationProxyFromJSONTyped(json, false); 41 | } 42 | 43 | export function IntegrationProxyFromJSONTyped(json: any, ignoreDiscriminator: boolean): IntegrationProxy { 44 | if ((json === undefined) || (json === null)) { 45 | return json; 46 | } 47 | return { 48 | 49 | 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], 50 | }; 51 | } 52 | 53 | export function IntegrationProxyToJSON(value?: IntegrationProxy | null): any { 54 | if (value === undefined) { 55 | return undefined; 56 | } 57 | if (value === null) { 58 | return null; 59 | } 60 | return { 61 | 62 | 'enabled': value.enabled, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/MediaTypeRegular.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * Media for light/regular mode. 18 | * @export 19 | * @interface MediaTypeRegular 20 | */ 21 | export interface MediaTypeRegular { 22 | /** 23 | * URL to the icon for the provider. 24 | * @type {string} 25 | * @memberof MediaTypeRegular 26 | */ 27 | iconURL?: string; 28 | /** 29 | * URL to the logo for the provider. 30 | * @type {string} 31 | * @memberof MediaTypeRegular 32 | */ 33 | logoURL?: string; 34 | } 35 | 36 | /** 37 | * Check if a given object implements the MediaTypeRegular interface. 38 | */ 39 | export function instanceOfMediaTypeRegular(value: object): boolean { 40 | let isInstance = true; 41 | 42 | return isInstance; 43 | } 44 | 45 | export function MediaTypeRegularFromJSON(json: any): MediaTypeRegular { 46 | return MediaTypeRegularFromJSONTyped(json, false); 47 | } 48 | 49 | export function MediaTypeRegularFromJSONTyped(json: any, ignoreDiscriminator: boolean): MediaTypeRegular { 50 | if ((json === undefined) || (json === null)) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'iconURL': !exists(json, 'iconURL') ? undefined : json['iconURL'], 56 | 'logoURL': !exists(json, 'logoURL') ? undefined : json['logoURL'], 57 | }; 58 | } 59 | 60 | export function MediaTypeRegularToJSON(value?: MediaTypeRegular | null): any { 61 | if (value === undefined) { 62 | return undefined; 63 | } 64 | if (value === null) { 65 | return null; 66 | } 67 | return { 68 | 69 | 'iconURL': value.iconURL, 70 | 'logoURL': value.logoURL, 71 | }; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/OptionalFieldsAutoOption.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | /** 17 | * 18 | * @export 19 | */ 20 | export const OptionalFieldsAutoOption = { 21 | All: 'all' 22 | } as const; 23 | export type OptionalFieldsAutoOption = typeof OptionalFieldsAutoOption[keyof typeof OptionalFieldsAutoOption]; 24 | 25 | 26 | export function OptionalFieldsAutoOptionFromJSON(json: any): OptionalFieldsAutoOption { 27 | return OptionalFieldsAutoOptionFromJSONTyped(json, false); 28 | } 29 | 30 | export function OptionalFieldsAutoOptionFromJSONTyped(json: any, ignoreDiscriminator: boolean): OptionalFieldsAutoOption { 31 | return json as OptionalFieldsAutoOption; 32 | } 33 | 34 | export function OptionalFieldsAutoOptionToJSON(value?: OptionalFieldsAutoOption | null): any { 35 | return value as any; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/ProjectEntitlementsBrandingRemoval.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * Whether branding can be removed from the UI library. 18 | * @export 19 | * @interface ProjectEntitlementsBrandingRemoval 20 | */ 21 | export interface ProjectEntitlementsBrandingRemoval { 22 | /** 23 | * Whether branding has been removed from the project. 24 | * @type {boolean} 25 | * @memberof ProjectEntitlementsBrandingRemoval 26 | */ 27 | value: boolean; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ProjectEntitlementsBrandingRemoval interface. 32 | */ 33 | export function instanceOfProjectEntitlementsBrandingRemoval(value: object): boolean { 34 | let isInstance = true; 35 | isInstance = isInstance && "value" in value; 36 | 37 | return isInstance; 38 | } 39 | 40 | export function ProjectEntitlementsBrandingRemovalFromJSON(json: any): ProjectEntitlementsBrandingRemoval { 41 | return ProjectEntitlementsBrandingRemovalFromJSONTyped(json, false); 42 | } 43 | 44 | export function ProjectEntitlementsBrandingRemovalFromJSONTyped(json: any, ignoreDiscriminator: boolean): ProjectEntitlementsBrandingRemoval { 45 | if ((json === undefined) || (json === null)) { 46 | return json; 47 | } 48 | return { 49 | 50 | 'value': json['value'], 51 | }; 52 | } 53 | 54 | export function ProjectEntitlementsBrandingRemovalToJSON(value?: ProjectEntitlementsBrandingRemoval | null): any { 55 | if (value === undefined) { 56 | return undefined; 57 | } 58 | if (value === null) { 59 | return null; 60 | } 61 | return { 62 | 63 | 'value': value.value, 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/ReadConfig.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | import type { ReadConfigObject } from './ReadConfigObject'; 17 | import { 18 | ReadConfigObjectFromJSON, 19 | ReadConfigObjectFromJSONTyped, 20 | ReadConfigObjectToJSON, 21 | } from './ReadConfigObject'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface ReadConfig 27 | */ 28 | export interface ReadConfig { 29 | /** 30 | * 31 | * @type {{ [key: string]: ReadConfigObject; }} 32 | * @memberof ReadConfig 33 | */ 34 | objects: { [key: string]: ReadConfigObject; }; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the ReadConfig interface. 39 | */ 40 | export function instanceOfReadConfig(value: object): boolean { 41 | let isInstance = true; 42 | isInstance = isInstance && "objects" in value; 43 | 44 | return isInstance; 45 | } 46 | 47 | export function ReadConfigFromJSON(json: any): ReadConfig { 48 | return ReadConfigFromJSONTyped(json, false); 49 | } 50 | 51 | export function ReadConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): ReadConfig { 52 | if ((json === undefined) || (json === null)) { 53 | return json; 54 | } 55 | return { 56 | 57 | 'objects': (mapValues(json['objects'], ReadConfigObjectFromJSON)), 58 | }; 59 | } 60 | 61 | export function ReadConfigToJSON(value?: ReadConfig | null): any { 62 | if (value === undefined) { 63 | return undefined; 64 | } 65 | if (value === null) { 66 | return null; 67 | } 68 | return { 69 | 70 | 'objects': (mapValues(value.objects, ReadConfigObjectToJSON)), 71 | }; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/SelectedFieldsAutoConfig.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | /** 17 | * If selectedFieldsAuto is set to all, all fields will be read. 18 | * @export 19 | */ 20 | export const SelectedFieldsAutoConfig = { 21 | SelectedFieldsAll: 'all' 22 | } as const; 23 | export type SelectedFieldsAutoConfig = typeof SelectedFieldsAutoConfig[keyof typeof SelectedFieldsAutoConfig]; 24 | 25 | 26 | export function SelectedFieldsAutoConfigFromJSON(json: any): SelectedFieldsAutoConfig { 27 | return SelectedFieldsAutoConfigFromJSONTyped(json, false); 28 | } 29 | 30 | export function SelectedFieldsAutoConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): SelectedFieldsAutoConfig { 31 | return json as SelectedFieldsAutoConfig; 32 | } 33 | 34 | export function SelectedFieldsAutoConfigToJSON(value?: SelectedFieldsAutoConfig | null): any { 35 | return value as any; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/UpdateOrgRequestOrg.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface UpdateOrgRequestOrg 20 | */ 21 | export interface UpdateOrgRequestOrg { 22 | /** 23 | * The organization label. 24 | * @type {string} 25 | * @memberof UpdateOrgRequestOrg 26 | */ 27 | label?: string; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the UpdateOrgRequestOrg interface. 32 | */ 33 | export function instanceOfUpdateOrgRequestOrg(value: object): boolean { 34 | let isInstance = true; 35 | 36 | return isInstance; 37 | } 38 | 39 | export function UpdateOrgRequestOrgFromJSON(json: any): UpdateOrgRequestOrg { 40 | return UpdateOrgRequestOrgFromJSONTyped(json, false); 41 | } 42 | 43 | export function UpdateOrgRequestOrgFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateOrgRequestOrg { 44 | if ((json === undefined) || (json === null)) { 45 | return json; 46 | } 47 | return { 48 | 49 | 'label': !exists(json, 'label') ? undefined : json['label'], 50 | }; 51 | } 52 | 53 | export function UpdateOrgRequestOrgToJSON(value?: UpdateOrgRequestOrg | null): any { 54 | if (value === undefined) { 55 | return undefined; 56 | } 57 | if (value === null) { 58 | return null; 59 | } 60 | return { 61 | 62 | 'label': value.label, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/ValueDefaults.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | /** 17 | * Configuration to set default write values for object fields. 18 | * @export 19 | * @interface ValueDefaults 20 | */ 21 | export interface ValueDefaults { 22 | /** 23 | * If true, users can set default values for any field. 24 | * @type {boolean} 25 | * @memberof ValueDefaults 26 | */ 27 | allowAnyFields?: boolean; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ValueDefaults interface. 32 | */ 33 | export function instanceOfValueDefaults(value: object): boolean { 34 | let isInstance = true; 35 | 36 | return isInstance; 37 | } 38 | 39 | export function ValueDefaultsFromJSON(json: any): ValueDefaults { 40 | return ValueDefaultsFromJSONTyped(json, false); 41 | } 42 | 43 | export function ValueDefaultsFromJSONTyped(json: any, ignoreDiscriminator: boolean): ValueDefaults { 44 | if ((json === undefined) || (json === null)) { 45 | return json; 46 | } 47 | return { 48 | 49 | 'allowAnyFields': !exists(json, 'allowAnyFields') ? undefined : json['allowAnyFields'], 50 | }; 51 | } 52 | 53 | export function ValueDefaultsToJSON(value?: ValueDefaults | null): any { 54 | if (value === undefined) { 55 | return undefined; 56 | } 57 | if (value === null) { 58 | return null; 59 | } 60 | return { 61 | 62 | 'allowAnyFields': value.allowAnyFields, 63 | }; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /generated-sources/api/src/models/WriteConfig.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Ampersand public API 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { exists, mapValues } from '../runtime'; 16 | import type { WriteConfigObject } from './WriteConfigObject'; 17 | import { 18 | WriteConfigObjectFromJSON, 19 | WriteConfigObjectFromJSONTyped, 20 | WriteConfigObjectToJSON, 21 | } from './WriteConfigObject'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface WriteConfig 27 | */ 28 | export interface WriteConfig { 29 | /** 30 | * 31 | * @type {{ [key: string]: WriteConfigObject; }} 32 | * @memberof WriteConfig 33 | */ 34 | objects?: { [key: string]: WriteConfigObject; }; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the WriteConfig interface. 39 | */ 40 | export function instanceOfWriteConfig(value: object): boolean { 41 | let isInstance = true; 42 | 43 | return isInstance; 44 | } 45 | 46 | export function WriteConfigFromJSON(json: any): WriteConfig { 47 | return WriteConfigFromJSONTyped(json, false); 48 | } 49 | 50 | export function WriteConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): WriteConfig { 51 | if ((json === undefined) || (json === null)) { 52 | return json; 53 | } 54 | return { 55 | 56 | 'objects': !exists(json, 'objects') ? undefined : (mapValues(json['objects'], WriteConfigObjectFromJSON)), 57 | }; 58 | } 59 | 60 | export function WriteConfigToJSON(value?: WriteConfig | null): any { 61 | if (value === undefined) { 62 | return undefined; 63 | } 64 | if (value === null) { 65 | return null; 66 | } 67 | return { 68 | 69 | 'objects': value.objects === undefined ? undefined : (mapValues(value.objects, WriteConfigObjectToJSON)), 70 | }; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /generated-sources/api/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "outDir": "dist/esm" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /generated-sources/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "outDir": "dist", 8 | "typeRoots": [ 9 | "node_modules/@types" 10 | ] 11 | }, 12 | "exclude": [ 13 | "dist", 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "6.6.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/fix-default-field-setting.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This script is used to fix the field setting type in the generated API. 3 | * The generated API uses '_default' instead of 'default' for the default value. 4 | * This script replaces '_default' with 'default' and "'_default'" with "'default'" in the generated API. 5 | */ 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | 9 | const rootDir = 'generated-sources/api'; 10 | 11 | function patchFieldSetting(filePath: string) { 12 | let original = fs.readFileSync(filePath, 'utf-8'); 13 | if (!original.includes('export interface FieldSetting ')) return; 14 | 15 | let updated = original; 16 | 17 | // 1. Rename `_default?: FieldSettingDefault` to `default?: FieldSettingDefault` 18 | updated = updated.replace(/_default\?\:\s*FieldSettingDefault/g, 'default?: FieldSettingDefault'); 19 | 20 | // 2. Replace `. _default` usage with `.default` 21 | updated = updated.replace(/value\._default/g, 'value.default'); 22 | 23 | // 3. Replace `_default` keys in return object (FromJSON/ToJSON) 24 | updated = updated.replace(/'_default'/g, "'default'"); 25 | updated = updated.replace(/_default:/g, 'default:'); 26 | 27 | fs.writeFileSync(filePath, updated); 28 | 29 | if (updated !== original) { 30 | fs.writeFileSync(filePath, updated); 31 | console.log(`✅ Patched _default to default): ${filePath}`); 32 | } else { 33 | console.log(`ℹ️ No changes needed: ${filePath}`); 34 | } 35 | } 36 | 37 | function walk(dir: string) { 38 | const entries = fs.readdirSync(dir, { withFileTypes: true }); 39 | for (const entry of entries) { 40 | const fullPath = path.join(dir, entry.name); 41 | if (entry.isDirectory()) walk(fullPath); 42 | else if (entry.name.endsWith('.ts')) patchFieldSetting(fullPath); 43 | } 44 | } 45 | 46 | walk(rootDir); 47 | -------------------------------------------------------------------------------- /src/assets/ErrorIcon.tsx: -------------------------------------------------------------------------------- 1 | export function ErrorIcon() { 2 | return ( 3 | 10 | 18 | 25 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/NavIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const CheckMarkIcon = ( 4 | 11 | 12 | 19 | 20 | ); 21 | 22 | export function NavIcon(completed: boolean, pending?: boolean) { 23 | if (pending) { 24 | return React.createElement( 25 | "svg", 26 | { 27 | xmlns: "http://www.w3.org/2000/svg", 28 | width: "18", 29 | height: "18", 30 | viewBox: "0 0 18 18", 31 | fill: "none", 32 | }, 33 | React.createElement("circle", { 34 | cx: "9", 35 | cy: "9", 36 | r: "5", 37 | fill: "#FBD38D", 38 | }), 39 | ); 40 | } 41 | if (completed) { 42 | return CheckMarkIcon; 43 | } 44 | // default to empty icon 45 | return ( 46 | 53 | 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/assets/PasswordEyeIcon.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | export function PasswordEyeIcon() { 3 | return ( 4 | 11 | 12 | 19 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/PasswordEyeSlashIcon.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | export function PasswordEyeSlashIcon() { 3 | return ( 4 | 11 | 18 | 25 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/SettingGearIcon.tsx: -------------------------------------------------------------------------------- 1 | export function SettingGearIcon() { 2 | return ( 3 | 10 | 17 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/SuccessIcon.tsx: -------------------------------------------------------------------------------- 1 | export function SuccessCheckmarkIcon() { 2 | return ( 3 | 10 | 11 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/TooltipIcon.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | export function TooltipIcon() { 3 | return ( 4 | 11 | 12 | 18 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/assets/TrashIcon.tsx: -------------------------------------------------------------------------------- 1 | // red.800 trash icon 2 | export function TrashIcon() { 3 | return ( 4 | 11 | 18 | 26 | 33 | 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Configure/ComponentContainer.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorTextBox } from "../ErrorTextBox/ErrorTextBox"; 2 | import { LoadingCentered } from "../Loading"; 3 | import { Box } from "../ui-base/Box/Box"; 4 | import { Container } from "../ui-base/Container/Container"; 5 | 6 | /** 7 | * Container (auto aligned center) with border to wrap some content. Usually the outer most container for 8 | * Ampersand public components. 9 | * @param param0 10 | * @returns 11 | */ 12 | export function ComponentContainer({ 13 | children, 14 | }: { 15 | children: React.ReactNode; 16 | }) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | 24 | export function ComponentContainerLoading() { 25 | return ( 26 | 27 | 28 | 29 | ); 30 | } 31 | 32 | /** 33 | * Container for error messages. Usually used when a component cannot be rendered due to an error. 34 | * @param param0 35 | * @returns 36 | */ 37 | export function ComponentContainerError({ 38 | message, 39 | children, 40 | }: { 41 | message: string; 42 | children?: React.ReactNode; 43 | }) { 44 | return ( 45 | 46 | {children} 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Configure/actions/mutateAndSetState/createInstallationAndSetState.ts: -------------------------------------------------------------------------------- 1 | import { 2 | api, 3 | Config, 4 | CreateInstallationOperationRequest, 5 | CreateInstallationRequestConfig, 6 | Installation, 7 | } from "services/api"; 8 | import { handleServerError } from "src/utils/handleServerError"; 9 | 10 | export type CreateInstallationSharedProps = { 11 | projectId: string; 12 | integrationId: string; 13 | groupRef: string; 14 | connectionId: string; 15 | apiKey: string; 16 | setError: (error: string) => void; 17 | setInstallation: (installationObj: Installation) => void; 18 | onInstallSuccess?: (installationId: string, config: Config) => void; 19 | }; 20 | 21 | type CreateInstallationAndSetStateProps = CreateInstallationSharedProps & { 22 | createConfig: CreateInstallationRequestConfig; 23 | }; 24 | 25 | export async function createInstallationAndSetState({ 26 | createConfig, 27 | projectId, 28 | integrationId, 29 | groupRef, 30 | connectionId, 31 | apiKey, 32 | setError, 33 | setInstallation, 34 | onInstallSuccess, 35 | }: CreateInstallationAndSetStateProps) { 36 | const createInstallationRequest: CreateInstallationOperationRequest = { 37 | projectIdOrName: projectId, 38 | integrationId, 39 | installation: { 40 | groupRef, 41 | connectionId, 42 | config: createConfig, 43 | }, 44 | }; 45 | 46 | try { 47 | const installation = await api().installationApi.createInstallation( 48 | createInstallationRequest, 49 | { 50 | headers: { 51 | "X-Api-Key": apiKey, 52 | "Content-Type": "application/json", 53 | }, 54 | }, 55 | ); 56 | setInstallation(installation); 57 | onInstallSuccess?.(installation.id, installation.config); 58 | } catch (error) { 59 | handleServerError(error, setError); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/Configure/actions/proxy/isProxyEnabled.ts: -------------------------------------------------------------------------------- 1 | import { HydratedRevision } from "services/api"; 2 | 3 | export function getIsProxyEnabled(hydratedRevision: HydratedRevision) { 4 | return hydratedRevision.content.proxy?.enabled; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Configure/actions/write/generateConfigWriteObjects.ts: -------------------------------------------------------------------------------- 1 | import { ConfigureState } from "../../types"; 2 | 3 | /** 4 | * example type 5 | * "objects": 6 | * { 7 | objects: { 8 | account: { 9 | objectName: 'account', 10 | selectedValueDefaults: { 11 | first: 'Joe', 12 | lastName: 'Default', 13 | }, 14 | }, 15 | contact: { 16 | objectName: 'contact', 17 | }, 18 | }, 19 | } 20 | * @param writeObjects 21 | * @param configureState 22 | * @returns 23 | */ 24 | export const generateConfigWriteObjects = (configureState: ConfigureState) => ({ 25 | ...configureState.write?.selectedWriteObjects, 26 | }); 27 | -------------------------------------------------------------------------------- /src/components/Configure/content/InstallationContent.tsx: -------------------------------------------------------------------------------- 1 | import { useInstallIntegrationProps } from "context/InstallIIntegrationContextProvider/InstallIntegrationContextProvider"; 2 | 3 | import { ErrorTextBox } from "../../ErrorTextBox/ErrorTextBox"; 4 | 5 | import { CreateInstallation } from "./CreateInstallation"; 6 | import { UpdateInstallation } from "./UpdateInstallation"; 7 | 8 | export function InstallationContent() { 9 | const { integrationObj, installation } = useInstallIntegrationProps(); 10 | 11 | if (!integrationObj) { 12 | return ; 13 | } 14 | 15 | return installation && integrationObj ? ( 16 | // If installation exists, render update installation flow 17 | 21 | ) : ( 22 | // No installation, render create installation flow 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Configure/content/README.md: -------------------------------------------------------------------------------- 1 | ### content folder 2 | This folder contains views and components related to the right content panel.This folder 3 | separates components from the nav sidebar on the left (ObjectManagementNav). -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldDefaultValueMapping/FieldDefaultValueMapping.tsx: -------------------------------------------------------------------------------- 1 | import { HydratedIntegrationWriteObject } from "@generated/api/src"; 2 | import { useHydratedRevision } from "src/components/Configure/state/HydratedRevisionContext"; 3 | 4 | import { useSelectedConfigureState } from "../../useSelectedConfigureState"; 5 | import { FieldHeader } from "../FieldHeader"; 6 | 7 | import { FieldDefaultValueTable } from "./FieldDefaultValueTable"; 8 | 9 | // checks if the object supports default values from hydratedRevision 10 | const isFieldDefaultValueSupported = ( 11 | objectName: string, 12 | writeObjects: HydratedIntegrationWriteObject[], 13 | ) => 14 | !!writeObjects.find( 15 | (writeObject) => 16 | writeObject.objectName === objectName && 17 | writeObject?.valueDefaults?.allowAnyFields, 18 | ); 19 | 20 | export function FieldDefaultValueMapping() { 21 | const { configureState } = useSelectedConfigureState(); 22 | 23 | const { writeObjects } = useHydratedRevision(); 24 | const selectedWriteObjects = configureState?.write?.selectedWriteObjects; 25 | const shouldRender = !!writeObjects; 26 | 27 | return ( 28 | shouldRender && ( 29 | <> 30 | {writeObjects.map((field) => { 31 | // only render default value if the object has write access. 32 | if ( 33 | selectedWriteObjects?.[field.objectName] && 34 | // check to hydrated revision for support - valueDefaults.allowAnyFields 35 | isFieldDefaultValueSupported(field.objectName, writeObjects) 36 | ) { 37 | return ( 38 | <> 39 | 40 | 41 | 42 | ); 43 | } 44 | return null; 45 | })} 46 | 47 | ) 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Divider } from "src/components/ui-base/Divider"; 2 | 3 | interface FieldHeaderProps { 4 | string: string; 5 | } 6 | 7 | export function FieldHeader({ string }: FieldHeaderProps) { 8 | return ( 9 |
17 |

24 | {string} 25 |

26 |
34 | 35 |
36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldMappings/DynamicFieldMappings.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl } from "src/components/form/FormControl"; 2 | import { 3 | HydratedIntegrationFieldExistent, 4 | IntegrationFieldMapping, 5 | } from "src/services/api"; 6 | 7 | import { FieldMappingRow } from "./FieldMappingRow"; 8 | 9 | export function DynamicFieldMappings({ 10 | dynamicFieldMappings, 11 | onSelectChange, 12 | allFields, 13 | }: { 14 | dynamicFieldMappings: IntegrationFieldMapping[]; 15 | onSelectChange: (e: React.ChangeEvent) => void; 16 | allFields: HydratedIntegrationFieldExistent[]; 17 | }) { 18 | return dynamicFieldMappings.length ? ( 19 |
20 | {dynamicFieldMappings.map((field) => ( 21 | 22 | 27 | 28 | ))} 29 |
30 | ) : null; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldMappings/checkDuplicateFieldError.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorBoundary } from "context/ErrorContextProvider"; 2 | import { SelectMappingFields } from "src/components/Configure/types"; 3 | 4 | interface DuplicateFieldErrorProps { 5 | selectedFieldMappings: SelectMappingFields | null | undefined; 6 | selectedObjectName: string | undefined; 7 | fieldName: string; 8 | fieldValue: string; 9 | setError: (boundary: ErrorBoundary, key: string, value: string[]) => void; 10 | } 11 | 12 | export function checkDuplicateFieldError({ 13 | selectedFieldMappings, 14 | selectedObjectName, 15 | fieldName, 16 | fieldValue, 17 | setError, 18 | }: DuplicateFieldErrorProps): boolean { 19 | if (!selectedFieldMappings || !selectedObjectName) return false; 20 | 21 | const hasDuplicate = Object.values(selectedFieldMappings).some( 22 | (mapping) => mapping === fieldValue && mapping !== fieldName, 23 | ); 24 | 25 | if (hasDuplicate) { 26 | console.error( 27 | "Each field must be mapped to a unique value", 28 | selectedFieldMappings, 29 | ); 30 | 31 | const duplicateFieldValues = Object.keys(selectedFieldMappings).filter( 32 | (key) => selectedFieldMappings[key] === fieldValue, 33 | ); 34 | 35 | // All the keys for which the duplicate value is found 36 | const keysForValue = [fieldName, ...duplicateFieldValues]; 37 | 38 | // set the error boundary keys for which duplicate values are found 39 | setError(ErrorBoundary.MAPPING, selectedObjectName, keysForValue); 40 | return true; 41 | } 42 | return false; 43 | } 44 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldMappings/index.ts: -------------------------------------------------------------------------------- 1 | import { FieldMappingRow } from "./FieldMappingRow"; 2 | import { OptionalFieldMappings } from "./OptionalFieldMappings"; 3 | import { RequiredFieldMappings } from "./RequiredFieldMappings"; 4 | 5 | export { 6 | FieldMappingRow as FieldMapping, 7 | RequiredFieldMappings, 8 | OptionalFieldMappings, 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/FieldMappings/setFieldMapping.ts: -------------------------------------------------------------------------------- 1 | import { Draft } from "immer"; 2 | 3 | import { isFieldObjectEqual } from "../../../state/utils"; 4 | import { ConfigureState } from "../../../types"; 5 | 6 | export type MappingFields = { 7 | field: string; 8 | value: string | null; 9 | }; 10 | 11 | function setFieldMappingProducer( 12 | draft: Draft, 13 | fields: Array, 14 | ) { 15 | const draftRequiredMapFields = draft?.read?.selectedFieldMappings || {}; 16 | 17 | fields.forEach((mapping) => { 18 | const { field, value } = mapping; 19 | if (value === null) { 20 | delete draftRequiredMapFields[field]; 21 | } else { 22 | draftRequiredMapFields[field] = value; 23 | } 24 | }); 25 | 26 | if (draft?.read) { 27 | const savedFields = draft.read.savedConfig.requiredMapFields; 28 | const updatedFields = draftRequiredMapFields; 29 | const isModified = !isFieldObjectEqual(savedFields, updatedFields); 30 | // immer exception if we try to set a value 31 | 32 | draft.read.isRequiredMapFieldsModified = isModified; 33 | } 34 | } 35 | 36 | export function setFieldMapping( 37 | selectedObjectName: string, 38 | setConfigureState: ( 39 | objectName: string, 40 | producer: (draft: Draft) => void, 41 | ) => void, 42 | fields: Array, 43 | ) { 44 | setConfigureState(selectedObjectName, (draft) => 45 | setFieldMappingProducer(draft, fields), 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/ObjectMapping.tsx: -------------------------------------------------------------------------------- 1 | import { FormCalloutBox } from "src/components/FormCalloutBox"; 2 | import { useProject } from "src/context/ProjectContextProvider"; 3 | import { useProvider } from "src/hooks/useProvider"; 4 | import { capitalize } from "src/utils"; 5 | 6 | import { useHydratedRevision } from "../../state/HydratedRevisionContext"; 7 | import { useSelectedConfigureState } from "../useSelectedConfigureState"; 8 | 9 | /** 10 | * ObjectMappingCallout component displays a callout box with the mapping information 11 | * @returns 12 | */ 13 | export function ReadObjectMapping() { 14 | const { project } = useProject(); 15 | const { hydratedRevision } = useHydratedRevision(); 16 | const { selectedObjectName } = useSelectedConfigureState(); 17 | 18 | const { providerName } = useProvider(); 19 | 20 | const appName = project?.appName; 21 | 22 | const selectedReadObject = hydratedRevision?.content?.read?.objects?.find( 23 | (obj) => obj.objectName === selectedObjectName, 24 | ); 25 | 26 | const objectDisplayName = 27 | selectedReadObject?.displayName || 28 | (selectedObjectName && capitalize(selectedObjectName)); 29 | 30 | const mapToName = selectedReadObject?.mapToName; 31 | const mapToDisplayName = 32 | selectedReadObject?.mapToDisplayName || 33 | (mapToName && capitalize(mapToName)); 34 | 35 | if (mapToDisplayName && appName && providerName) { 36 | return ( 37 | 38 |

39 | {mapToDisplayName} in {appName} is mapped to{" "} 40 | {objectDisplayName} in {providerName}. 41 |

42 |
43 | ); 44 | } 45 | 46 | // renders no callout if there is no mapping 47 | return null; 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/OptionalFields/OptionalFields.tsx: -------------------------------------------------------------------------------- 1 | import { OptionalFieldsV2 } from "./OptionalFieldsV2"; 2 | 3 | /** 4 | * Bridge component to OptionalFieldsV2 5 | * @returns 6 | */ 7 | export function OptionalFields() { 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/OptionalFields/index.ts: -------------------------------------------------------------------------------- 1 | import { OptionalFields } from "./OptionalFields"; 2 | 3 | export { OptionalFields }; 4 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/OptionalFields/optionalFields.module.css: -------------------------------------------------------------------------------- 1 | .checkboxGroupContainer { 2 | margin-bottom: 10px; 3 | border: 1px solid var(--amp-colors-border); 4 | border-radius: var(--amp-default-border-radius); 5 | } 6 | .stack { 7 | overflow-y: scroll; 8 | max-height: 300px; 9 | } 10 | 11 | .selectAllContainer { 12 | background-color: var(--amp-colors-bg-highlight); 13 | padding: 8px 10px; 14 | display: flex; 15 | align-items: center; 16 | } 17 | 18 | .fieldContainer { 19 | padding: 8px 10px; 20 | border-bottom: 1px solid var(--amp-colors-border); 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .checkbox { 26 | margin-right: 10px; 27 | background-color: var(--amp-colors-bg-primary); 28 | width: 20px; 29 | height: 20px; 30 | border-radius: 4px; 31 | display: flex; 32 | justify-content: center; 33 | border: 1px solid var(--amp-colors-input-border-default); 34 | cursor: pointer; 35 | flex: none; 36 | } 37 | 38 | .checkbox:hover { 39 | background-color: var(--amp-colors-input-surface-hover); 40 | border-color: var(--amp-colors-input-border-hover); 41 | } 42 | 43 | .checkbox:focus { 44 | border-color: var(--amp-colors-input-border-focus); 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/OptionalFields/setOptionalField.ts: -------------------------------------------------------------------------------- 1 | import { Draft } from "immer"; 2 | 3 | import { isFieldObjectEqual } from "../../../state/utils"; 4 | import { ConfigureState } from "../../../types"; 5 | 6 | function setOptionalFieldProducer( 7 | draft: Draft, 8 | fieldKey: string, 9 | checked: boolean, 10 | ) { 11 | const draftSelectedOptionalFields = draft?.read?.selectedOptionalFields || {}; 12 | draftSelectedOptionalFields[fieldKey] = checked; 13 | if (!checked) { 14 | delete draftSelectedOptionalFields[fieldKey]; 15 | } 16 | 17 | if (draft.read?.savedConfig.optionalFields) { 18 | const savedFields = draft.read.savedConfig.optionalFields; 19 | const updatedFields = draftSelectedOptionalFields; 20 | const isModified = !isFieldObjectEqual(savedFields, updatedFields); 21 | // immer exception if we try to set a value 22 | 23 | draft.read.isOptionalFieldsModified = isModified; 24 | } else { 25 | console.warn("read.savedConfig.optionalFields is undefined"); 26 | } 27 | } 28 | 29 | export function setOptionalField( 30 | selectedObjectName: string, 31 | setConfigureState: ( 32 | objectName: string, 33 | producer: (draft: Draft) => void, 34 | ) => void, 35 | fieldKey: string, 36 | checked: boolean, 37 | ) { 38 | setConfigureState(selectedObjectName, (draft) => 39 | setOptionalFieldProducer(draft, fieldKey, checked), 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/ReadFields.tsx: -------------------------------------------------------------------------------- 1 | import { OptionalFieldMappings, RequiredFieldMappings } from "./FieldMappings"; 2 | import { useClearOldFieldMappings } from "./FieldMappings/useClearOldFieldMappings"; 3 | import { ReadObjectMapping } from "./ObjectMapping"; 4 | import { OptionalFields } from "./OptionalFields"; 5 | import { RequiredFields } from "./RequiredFields"; 6 | import { ValueMappings } from "./ValueMapping"; 7 | 8 | export function ReadFields() { 9 | useClearOldFieldMappings(); 10 | 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/RequiredFields.tsx: -------------------------------------------------------------------------------- 1 | import { useProject } from "context/ProjectContextProvider"; 2 | 3 | import { Tag } from "components/ui-base/Tag"; 4 | 5 | import { isIntegrationFieldMapping } from "../../utils"; 6 | import { useSelectedConfigureState } from "../useSelectedConfigureState"; 7 | 8 | import { FieldHeader } from "./FieldHeader"; 9 | 10 | export function RequiredFields() { 11 | const { configureState } = useSelectedConfigureState(); 12 | const { appName } = useProject(); 13 | 14 | return ( 15 | <> 16 | 17 |
25 | {configureState?.read?.requiredFields?.length 26 | ? configureState.read?.requiredFields.map((field) => { 27 | if (!isIntegrationFieldMapping(field)) { 28 | return {field.displayName}; 29 | } 30 | return null; // fallback for customed mapped fields 31 | }) 32 | : "There are no required fields."} 33 |
34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/ValueMapping/ValueHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Divider } from "src/components/ui-base/Divider"; 2 | 3 | interface ValueHeaderProps { 4 | string: string; 5 | fieldName: string; 6 | } 7 | 8 | export function ValueHeader({ string, fieldName }: ValueHeaderProps) { 9 | return ( 10 |
18 |

25 | {string} 26 | {fieldName} 27 |

28 |
36 | 37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/ValueMapping/index.tsx: -------------------------------------------------------------------------------- 1 | import { ValueMappings } from "./ValuesMapping"; 2 | 3 | export { ValueMappings }; 4 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/ValueMapping/setValueMapping.ts: -------------------------------------------------------------------------------- 1 | import { Draft } from "immer"; 2 | 3 | import { ConfigureState } from "../../../types"; 4 | 5 | function setValueMappingProducer( 6 | draft: Draft, 7 | selectedObjectName: string, 8 | sourceValue: string, 9 | targetValue: string, 10 | fieldName: string, 11 | ) { 12 | // Ensure structure exists 13 | if (!draft.read) { 14 | return; 15 | } 16 | if (!draft.read.selectedValueMappings) { 17 | draft.read.selectedValueMappings = {}; 18 | } 19 | 20 | if (!draft.read.selectedValueMappings[fieldName]) { 21 | draft.read.selectedValueMappings[fieldName] = {}; 22 | } 23 | 24 | // Directly mutate the draft 25 | if ( 26 | targetValue === "" && 27 | draft.read.selectedValueMappings[fieldName][sourceValue] 28 | ) { 29 | delete draft.read.selectedValueMappings[fieldName][sourceValue]; 30 | } else { 31 | draft.read.selectedValueMappings[fieldName][sourceValue] = targetValue; 32 | } 33 | } 34 | 35 | export function setValueMapping( 36 | selectedObjectName: string, 37 | setConfigureState: ( 38 | objectName: string, 39 | producer: (draft: Draft) => void, 40 | ) => void, 41 | sourceValue: string, 42 | targetValue: string, 43 | fieldName: string, 44 | ) { 45 | setConfigureState(selectedObjectName, (draft) => 46 | setValueMappingProducer( 47 | draft, 48 | selectedObjectName, 49 | sourceValue, 50 | targetValue, 51 | fieldName, 52 | ), 53 | ); 54 | } 55 | 56 | export function setValueMappingModified( 57 | selectedObjectName: string, 58 | setConfigureState: ( 59 | objectName: string, 60 | producer: (draft: Draft) => void, 61 | ) => void, 62 | isModified: boolean, 63 | ) { 64 | setConfigureState(selectedObjectName, (draft) => { 65 | if (draft.read) { 66 | draft.read.isValueMappingsModified = isModified; 67 | } 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/WriteFields/WriteFields.tsx: -------------------------------------------------------------------------------- 1 | // import { FieldDefaultValueMapping } from '../FieldDefaultValueMapping/FieldDefaultValueMapping'; 2 | 3 | import { WriteFieldsV2 } from "./WriteFieldsV2"; 4 | 5 | /** 6 | * Bridge component to WriteFieldsV2 7 | * @returns 8 | */ 9 | export function WriteFields() { 10 | return ( 11 | <> 12 | 13 | {/* TODO(ENG-1970): Bring this back when new valueDefault format is supported */} 14 | {/* */} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/WriteFields/index.ts: -------------------------------------------------------------------------------- 1 | import { WriteFields } from "./WriteFields"; 2 | 3 | export { WriteFields }; 4 | -------------------------------------------------------------------------------- /src/components/Configure/content/fields/WriteFields/setNonConfigurableWriteField.tsx: -------------------------------------------------------------------------------- 1 | import { Draft } from "immer"; 2 | 3 | import { areWriteObjectsEqual } from "../../../state/utils"; 4 | import { ConfigureState } from "../../../types"; 5 | 6 | function setNonConfigurableWriteFieldProducer( 7 | draft: Draft, 8 | fieldKey: string, 9 | checked: boolean, 10 | ) { 11 | if (draft?.write?.selectedWriteObjects === null) { 12 | // immer syntax to set a value 13 | 14 | draft.write.selectedWriteObjects = {}; 15 | } 16 | 17 | if (draft?.write) { 18 | const draftSelectedWriteFields = draft.write.selectedWriteObjects; 19 | if (checked) { 20 | draftSelectedWriteFields[fieldKey] = { objectName: fieldKey }; 21 | } 22 | 23 | if (!checked) { 24 | delete draftSelectedWriteFields[fieldKey]; 25 | } 26 | 27 | // check is modified 28 | if (draft?.write?.savedConfig?.selectedWriteObjects) { 29 | const savedWriteObjects = draft.write.savedConfig.selectedWriteObjects; 30 | const updatedWriteObjects = draftSelectedWriteFields; 31 | const isModified = !areWriteObjectsEqual( 32 | savedWriteObjects, 33 | updatedWriteObjects, 34 | ); 35 | // immer syntax to set a value 36 | 37 | draft.write.isWriteModified = isModified; 38 | } 39 | 40 | // DEBUG: print out the draft 41 | // console.debug(JSON.stringify(draft, null, 2)); 42 | } 43 | } 44 | 45 | export function setNonConfigurableWriteField( 46 | selectedObjectName: string, 47 | setConfigureState: ( 48 | objectName: string, 49 | producer: (draft: Draft) => void, 50 | ) => void, 51 | fieldKey: string, 52 | checked: boolean, 53 | ) { 54 | setConfigureState(selectedObjectName, (draft) => { 55 | setNonConfigurableWriteFieldProducer(draft, fieldKey, checked); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /src/components/Configure/content/manage/AuthenticationSection.tsx: -------------------------------------------------------------------------------- 1 | import { useConnections } from "src/context/ConnectionsContextProvider"; 2 | import { useProvider } from "src/hooks/useProvider"; 3 | import { capitalize } from "src/utils"; 4 | 5 | import { FieldHeader } from "../fields/FieldHeader"; 6 | 7 | import styles from "./authenticate.module.css"; 8 | 9 | function AuthenticationRow({ 10 | label, 11 | value, 12 | }: { 13 | label: string; 14 | value: string | undefined; 15 | }) { 16 | return ( 17 |
18 |
{label}
19 |
{value}
20 |
21 | ); 22 | } 23 | export function AuthenticationSection() { 24 | const { selectedConnection } = useConnections(); 25 | const { providerName } = useProvider(); 26 | const isSalesforce = selectedConnection?.provider === "salesforce"; 27 | const workspaceString = isSalesforce ? "subdomain" : "workspace"; 28 | const workspaceLabel = `${providerName} ${workspaceString}`; 29 | 30 | return ( 31 | <> 32 | 33 |
34 | 38 | 42 |
43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Configure/content/manage/ManageContent.tsx: -------------------------------------------------------------------------------- 1 | import { useInstallIntegrationProps } from "context/InstallIIntegrationContextProvider/InstallIntegrationContextProvider"; 2 | 3 | import { RemoveConnectionSection } from "components/Connect/RemoveConnectionSection"; 4 | 5 | import { AuthenticationSection } from "./AuthenticationSection"; 6 | import { UninstallSection } from "./UninstallSection"; 7 | import { UpdateConnectionSection } from "./updateConnection/UpdateConnectionSection"; 8 | /** 9 | * ManageContent is the content for the manage tab. 10 | * It displays the connection details, update the connection details, and uninstall the integration. 11 | * @returns 12 | */ 13 | export function ManageContent() { 14 | const { installation, resetComponent } = useInstallIntegrationProps(); 15 | 16 | return ( 17 | <> 18 | 19 | 20 | {/* Uninstall section is only shown if the integration is installed */} 21 | {installation && } 22 | {/* Remove connection section is only shown if the integration is not installed */} 23 | {!installation && ( 24 | 25 | )} 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Configure/content/manage/UninstallSection.tsx: -------------------------------------------------------------------------------- 1 | import { UninstallButton } from "src/components/Configure/layout/UninstallButton"; 2 | import { useInstallIntegrationProps } from "src/context/InstallIIntegrationContextProvider/InstallIntegrationContextProvider"; 3 | import { useProvider } from "src/hooks/useProvider"; 4 | 5 | import { FieldHeader } from "../fields/FieldHeader"; 6 | 7 | export function UninstallSection() { 8 | const { installation } = useInstallIntegrationProps(); 9 | const { providerName } = useProvider(); 10 | 11 | // cannot uninstall if installation is not found 12 | if (!installation) return null; 13 | 14 | return ( 15 | <> 16 | 17 |
25 | {installation?.id ? ( 26 |

27 | By clicking below, you will uninstall the entire {providerName}{" "} 28 | integration. You will lose any configuration you've set up. 29 |

30 | ) : ( 31 |

You've successfully uninstalled the integration.

32 | )} 33 | 34 |
35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Configure/content/manage/authenticate.module.css: -------------------------------------------------------------------------------- 1 | .authenticationRow { 2 | display: flex; 3 | justify-content: space-between; 4 | padding: 0.5rem 0; 5 | font-size: 1rem; 6 | } 7 | 8 | .field { 9 | color: var(--amp-colors-text-muted); 10 | } 11 | 12 | .value { 13 | color: var(--amp-colors-text-regular); 14 | text-align: right; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/components/Configure/content/manage/updateConnection/UpdateConnectionSection.tsx: -------------------------------------------------------------------------------- 1 | import { AuthType, Oauth2OptsGrantTypeEnum } from "@generated/api/src"; 2 | import { useProviderInfoQuery } from "src/hooks/useProvider"; 3 | 4 | import { UpdateApiKeyConnect } from "./UpdateApiKeyConnect"; 5 | import { UpdateBasicAuthConnect } from "./UpdateBasicAuthConnect"; 6 | import { UpdateClientCredentialsConnect } from "./UpdateClientCredentialsConnect"; 7 | import { UpdateOauthConnect } from "./UpdateOauthConnect"; 8 | 9 | /** 10 | * 11 | * @param provider is provided directly for ConnectProvider component 12 | * @returns 13 | */ 14 | export function UpdateConnectionSection({ provider }: { provider?: string }) { 15 | const { data: providerInfo } = useProviderInfoQuery(provider); 16 | 17 | if (providerInfo?.authType === AuthType.Basic) { 18 | return ; 19 | } 20 | 21 | if (providerInfo?.authType === AuthType.ApiKey) { 22 | return ; 23 | } 24 | 25 | // if oauth2 and supported grant type, oauth update connection 26 | const oAuthGrantType = providerInfo?.oauth2Opts?.grantType; 27 | if (oAuthGrantType) { 28 | if ( 29 | oAuthGrantType === Oauth2OptsGrantTypeEnum.AuthorizationCode || 30 | oAuthGrantType === Oauth2OptsGrantTypeEnum.AuthorizationCodePkce 31 | ) { 32 | return ; 33 | } 34 | 35 | if (oAuthGrantType === Oauth2OptsGrantTypeEnum.ClientCredentials) { 36 | return ; 37 | } 38 | } 39 | 40 | return null; 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Configure/content/useSelectedConfigureState.tsx: -------------------------------------------------------------------------------- 1 | import { useProject } from "context/ProjectContextProvider"; 2 | 3 | import { useSelectedObjectName } from "../nav/ObjectManagementNav/ObjectManagementNavContext"; 4 | import { useObjectsConfigureState } from "../state/ConfigurationStateProvider"; 5 | import { getConfigureState } from "../state/utils"; 6 | 7 | /** 8 | * 9 | * @returns {object} configureState for the selected object in nav 10 | */ 11 | export const useSelectedConfigureState = () => { 12 | const { appName } = useProject(); 13 | const { objectConfigurationsState, setConfigureState } = 14 | useObjectsConfigureState(); 15 | const { selectedObjectName } = useSelectedObjectName(); 16 | const configureState = getConfigureState( 17 | selectedObjectName || "", 18 | objectConfigurationsState, 19 | ); 20 | 21 | return { 22 | appName, 23 | configureState, 24 | setConfigureState, 25 | selectedObjectName, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/Configure/index.ts: -------------------------------------------------------------------------------- 1 | export { InstallIntegration } from "./InstallIntegration"; 2 | -------------------------------------------------------------------------------- /src/components/Configure/layout/ConditionalHasConfigurationLayout/InstalledSuccessBox.tsx: -------------------------------------------------------------------------------- 1 | import { useProvider } from "src/hooks/useProvider"; 2 | 3 | import { SuccessTextBox } from "components/SuccessTextBox/SuccessTextBox"; 4 | 5 | import { UninstallButton } from "../UninstallButton"; 6 | 7 | type InstalledSuccessBoxProps = { 8 | provider: string; 9 | }; 10 | 11 | export function InstalledSuccessBox({ provider }: InstalledSuccessBoxProps) { 12 | const { providerName } = useProvider(provider); 13 | const text = `You have successfully installed your ${providerName} integration.`; 14 | return ( 15 | 16 |
17 | 22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/NavObjectItemProps.tsx: -------------------------------------------------------------------------------- 1 | export interface NavObjectItemProps { 2 | objectName: string; 3 | completed: boolean; 4 | pending?: boolean; 5 | displayName?: string; // overrides objectName as the display name when present 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/ObjectManagementNavContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "react"; 2 | 3 | // Create a context for the selected navObject's name 4 | 5 | export const SelectedObjectNameContext = createContext< 6 | string | null | undefined 7 | >(null); 8 | // Custom hook to access the selected navObject's name 9 | 10 | export function useSelectedObjectName() { 11 | const selectedNavObjectName = useContext(SelectedObjectNameContext); 12 | if (selectedNavObjectName === null) { 13 | throw new Error( 14 | "useSelectedNavObjectName must be used within a SelectedNavObjectNameProvider", 15 | ); 16 | } 17 | return { selectedObjectName: selectedNavObjectName }; // Return as an object 18 | } 19 | // create context for setNextTabIndex function 20 | 21 | export const NextTabIndexContext = createContext<() => void>(() => {}); 22 | // Custom hook to access the setNextTabIndex function 23 | 24 | export function useNextIncompleteTabIndex() { 25 | const onNextIncompleteTab = useContext(NextTabIndexContext); 26 | if (!onNextIncompleteTab) { 27 | throw new Error( 28 | "useSetNextTabIndex must be used within a NextTabIndexProvider", 29 | ); 30 | } 31 | return { onNextIncompleteTab }; 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/constant.tsx: -------------------------------------------------------------------------------- 1 | // WRITE_CONST is used for the "Write" tab, its value is "other" for legacy reasons. 2 | export const WRITE_CONST = "other"; 3 | // MANAGE_TAB_CONST is used for the "Manage" tab. 4 | export const MANAGE_TAB_CONST = "manage-tab"; 5 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/index.tsx: -------------------------------------------------------------------------------- 1 | import { ObjectManagementNavV2 } from "./v2"; 2 | 3 | type ObjectManagementNavProps = { 4 | children?: React.ReactNode; 5 | }; 6 | 7 | /** 8 | * Bridge component to ObjectManagementNavV2 9 | * @param param0 10 | * @returns 11 | */ 12 | export function ObjectManagementNav({ ...props }: ObjectManagementNavProps) { 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/v2/Tabs/ManageTab.tsx: -------------------------------------------------------------------------------- 1 | import * as Tabs from "@radix-ui/react-tabs"; 2 | import { SettingGearIcon } from "assets/SettingGearIcon"; 3 | import { Divider } from "src/components/ui-base/Divider"; 4 | 5 | import { MANAGE_TAB_CONST } from "../../constant"; 6 | 7 | import styles from "./tabs.module.css"; 8 | 9 | export function ManageTab() { 10 | return ( 11 | <> 12 | 13 | 14 |
22 | 23 | Manage 24 |
25 |
26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Configure/nav/ObjectManagementNav/v2/Tabs/tabs.module.css: -------------------------------------------------------------------------------- 1 | .tabsRoot { 2 | display: flex; 3 | width: 100%; 4 | } 5 | 6 | .tabsList { 7 | display: flex; 8 | flex-direction: column; 9 | width: 100%; 10 | } 11 | 12 | .tabTrigger { 13 | all: unset; 14 | padding: 10px; 15 | cursor: pointer; 16 | background-color: var(--amp-colors-tabs-background); 17 | border-radius: var(--amp-default-border-radius); 18 | margin-bottom: 5px; 19 | } 20 | 21 | .tabTrigger:hover { 22 | background-color: var(--amp-colors-bg-highlight); 23 | } 24 | 25 | .tabTrigger[data-state="active"] { 26 | background-color: var(--amp-colors-bg-highlight); 27 | } 28 | 29 | .danger { 30 | color: var(--amp-colors-status-critical); 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/components/Connect/ConnectedSuccessBox.tsx: -------------------------------------------------------------------------------- 1 | import { useProject } from "context/ProjectContextProvider"; 2 | import { useProvider } from "src/hooks/useProvider"; 3 | import { Connection } from "src/services/api"; 4 | 5 | import { SuccessTextBox } from "../SuccessTextBox/SuccessTextBox"; 6 | 7 | import { ManageConnectionSection } from "./ManageConnectionSection"; 8 | 9 | interface ConnectedSuccessBoxProps { 10 | resetComponent: () => void; // reset the ConnectProvider component 11 | provider: string; 12 | onDisconnectSuccess?: (connection: Connection) => void; 13 | } 14 | export function ConnectedSuccessBox({ 15 | provider, 16 | onDisconnectSuccess, 17 | resetComponent, 18 | }: ConnectedSuccessBoxProps) { 19 | const { appName } = useProject(); 20 | const { providerName } = useProvider(provider); 21 | 22 | const text = `You have successfully connected your ${providerName} account to ${appName}.`; 23 | return ( 24 | 25 |
33 | 38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Connect/ManageConnectionSection.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { UpdateConnectionSection } from "src/components/Configure/content/manage/updateConnection/UpdateConnectionSection"; 3 | import { Connection } from "src/services/api"; 4 | 5 | import { FieldHeader } from "../Configure/content/fields/FieldHeader"; 6 | import { Button } from "../ui-base/Button"; 7 | 8 | import { RemoveConnectionSection } from "./RemoveConnectionSection"; 9 | 10 | export function ManageConnectionSection({ 11 | resetComponent, 12 | onDisconnectSuccess, 13 | provider, 14 | }: { 15 | resetComponent: () => void; 16 | onDisconnectSuccess?: (connection: Connection) => void; 17 | provider?: string; 18 | }) { 19 | const [showUpdateConnection, setShowUpdateConnection] = useState(false); 20 | 21 | return ( 22 | <> 23 | {showUpdateConnection === false && ( 24 | <> 25 | 26 | {/* Mimic style of UpdateConnectionSection */} 27 |
36 |

Click to reauthenticate or refresh your connection.

37 | 46 |
47 | 48 | )} 49 | {showUpdateConnection === true && ( 50 | <> 51 | 52 | 56 | 57 | )} 58 | 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Connect/RemoveConnectionSection.tsx: -------------------------------------------------------------------------------- 1 | import { Connection } from "src/services/api"; 2 | 3 | import { FieldHeader } from "../Configure/content/fields/FieldHeader"; 4 | 5 | import { RemoveConnectionButton } from "./RemoveConnectionButton"; 6 | 7 | interface RemoveConnectionSectionProps { 8 | resetComponent: () => void; 9 | onDisconnectSuccess?: (connection: Connection) => void; 10 | } 11 | 12 | export function RemoveConnectionSection({ 13 | resetComponent, 14 | onDisconnectSuccess, 15 | }: RemoveConnectionSectionProps) { 16 | return ( 17 |
18 | 19 |

Click to disconnect your connection from the provider.

20 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Connect/useConnectionHandler.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useConnections } from "context/ConnectionsContextProvider"; 3 | import { Connection } from "src/services/api"; 4 | 5 | function useOnSuccessHandler(onSuccess?: (connection: Connection) => void) { 6 | const { selectedConnection } = useConnections(); 7 | useEffect(() => { 8 | // Check if a onSuccess callback is present 9 | if (onSuccess && selectedConnection) { 10 | // call callback when connection is selected 11 | onSuccess(selectedConnection); 12 | } 13 | }, [onSuccess, selectedConnection]); 14 | } 15 | 16 | type ConnectionHandlerPropsProps = { 17 | onSuccess?: (connection: Connection) => void; 18 | // onError?: (error: string) => void; // not supported yet 19 | }; 20 | 21 | /** 22 | * ConnectionHandler is a component that handles onSuccess and onError callbacks 23 | * @param onSuccess - callback function to be called when a connection is successful 24 | * @returns 25 | */ 26 | export function useConnectionHandler({ 27 | onSuccess, 28 | }: ConnectionHandlerPropsProps) { 29 | useOnSuccessHandler(onSuccess); 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Docs/DocsHelperText.tsx: -------------------------------------------------------------------------------- 1 | import { AccessibleLink } from "../ui-base/AccessibleLink"; 2 | 3 | type DocsHelperTextProps = { 4 | url: string; 5 | providerDisplayName: string; 6 | credentialName: string; 7 | }; 8 | 9 | export function DocsHelperText({ 10 | url, 11 | providerDisplayName, 12 | credentialName, 13 | }: DocsHelperTextProps) { 14 | return ( 15 |

16 | 17 | Learn more 18 | {" "} 19 | about where to find your {providerDisplayName} {credentialName}. 20 |

21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/ErrorTextBox/ErrorTextBox.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorIcon } from "assets/ErrorIcon"; 2 | 3 | import { Box } from "../ui-base/Box/Box"; 4 | import { Container } from "../ui-base/Container/Container"; 5 | 6 | import classes from "./errorTextBox.module.css"; 7 | 8 | interface ErrorTextBoxProps { 9 | message: string; 10 | children?: React.ReactNode; 11 | } 12 | 13 | export function InnerErrorTextBox({ message }: { message: string }) { 14 | return ( 15 | 16 |

{message}

17 |
18 | ); 19 | } 20 | 21 | export function ErrorTextBox({ message, children }: ErrorTextBoxProps) { 22 | return ( 23 | 24 | 25 | 26 | {children} 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/ErrorTextBox/errorTextBox.module.css: -------------------------------------------------------------------------------- 1 | .errorBoxContainer { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-evenly; 5 | align-items: center; 6 | padding: 30px; 7 | min-height: 300px; 8 | } 9 | 10 | .errorBox { 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | padding: 1em 2rem; 15 | border-radius: var(--amp-default-border-radius); 16 | background-color: var(--amp-colors-status-critical-muted); 17 | border: 1px solid var(--amp-colors-status-critical-muted); 18 | } 19 | 20 | .errorText { 21 | color: var(--amp-colors-status-critical-dark); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/FormCalloutBox.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from "src/components/ui-base/Box/Box"; 2 | 3 | const defaultStyle = { 4 | backgroundColor: "var(--amp-colors-bg-highlight)", 5 | borderColor: "var(--amp-colors-bg-highlight)", 6 | padding: ".5rem 1rem", 7 | }; 8 | 9 | type FormCalloutBoxProps = { 10 | children: React.ReactNode; 11 | style?: React.CSSProperties; 12 | }; 13 | 14 | export function FormCalloutBox({ children, style }: FormCalloutBoxProps) { 15 | return {children}; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/FormErrorBox.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from "src/components/ui-base/Box/Box"; 2 | 3 | const defaultStyle = { 4 | backgroundColor: "var(--amp-colors-status-critical-muted)", 5 | borderColor: "var(--amp-colors-status-critical-muted)", 6 | color: "var(--amp-colors-status-critical-dark)", 7 | padding: ".5rem 1rem", 8 | }; 9 | 10 | type FormErrorBoxProps = { 11 | children: React.ReactNode; 12 | style?: React.CSSProperties; 13 | }; 14 | 15 | export function FormErrorBox({ children, style }: FormErrorBoxProps) { 16 | return {children}; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/FormSuccessBox.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from "src/components/ui-base/Box/Box"; 2 | 3 | const defaultStyle = { 4 | backgroundColor: "var(--amp-colors-status-success-muted)", 5 | borderColor: "var(--amp-colors-status-success-muted)", 6 | color: "var(--amp-colors-status-success-dark)", 7 | padding: ".5rem 1rem", 8 | }; 9 | 10 | type FormSuccessBoxProps = { 11 | children: React.ReactNode; 12 | style?: React.CSSProperties; 13 | }; 14 | 15 | export function FormSuccessBox({ children, style }: FormSuccessBoxProps) { 16 | return {children}; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Loading/index.tsx: -------------------------------------------------------------------------------- 1 | import classes from "./style.module.css"; 2 | 3 | /** 4 | * supported after removing chakra-ui 5 | * @returns 6 | */ 7 | function Loading() { 8 | return ; 9 | } 10 | 11 | /** 12 | * replaces LoadingIcon with a simple spinner centered in div 13 | * @returns 14 | */ 15 | export function LoadingCentered() { 16 | return ( 17 |
27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Loading/style.module.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | animation: rotate 1s infinite; 3 | height: 50px; 4 | width: 50px; 5 | } 6 | 7 | .loader:before, 8 | .loader:after { 9 | border-radius: 50%; 10 | content: ""; 11 | display: block; 12 | height: 20px; 13 | width: 20px; 14 | } 15 | .loader:before { 16 | animation: ball1 1s infinite; 17 | background-color: var(--amp-colors-primary); 18 | box-shadow: 30px 0 0 var(--amp-colors-neutral-700); 19 | margin-bottom: 10px; 20 | } 21 | .loader:after { 22 | animation: ball2 1s infinite; 23 | background-color: var(--amp-colors-neutral-700); 24 | box-shadow: 30px 0 0 var(--amp-colors-primary); 25 | } 26 | 27 | @keyframes rotate { 28 | 0% { transform: rotate(0deg) scale(0.8) } 29 | 50% { transform: rotate(360deg) scale(1.2) } 30 | 100% { transform: rotate(720deg) scale(0.8) } 31 | } 32 | 33 | @keyframes ball1 { 34 | 0% { 35 | box-shadow: 30px 0 0 var(--amp-colors-neutral-700); 36 | } 37 | 50% { 38 | box-shadow: 0 0 0 var(--amp-colors-neutral-700); 39 | margin-bottom: 0; 40 | transform: translate(15px, 15px); 41 | } 42 | 100% { 43 | box-shadow: 30px 0 0 var(--amp-colors-neutral-700); 44 | margin-bottom: 10px; 45 | } 46 | } 47 | 48 | @keyframes ball2 { 49 | 0% { 50 | box-shadow: 30px 0 0 var(--amp-colors-primary); 51 | } 52 | 50% { 53 | box-shadow: 0 0 0 var(--amp-colors-primary); 54 | margin-top: -20px; 55 | transform: translate(15px, 15px); 56 | } 57 | 100% { 58 | box-shadow: 30px 0 0 var(--amp-colors-primary); 59 | margin-top: 0; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/RedirectHandler/RedirectHandler.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | 3 | import { RedirectLoading } from "./RedirectLoading"; 4 | 5 | type RedirectHandlerProps = { 6 | redirectURL?: string; 7 | children: React.ReactNode; 8 | }; 9 | 10 | /** 11 | * RedirectHandler is a component that redirects to a specified URL when mounted or 12 | * will render the children if no redirect URL is present. 13 | * 14 | * @param redirectURL 15 | * @param children 16 | * @returns 17 | */ 18 | export function RedirectHandler({ 19 | redirectURL, 20 | children, 21 | }: RedirectHandlerProps) { 22 | useEffect(() => { 23 | // Check if a redirect URL is present 24 | if (redirectURL) { 25 | // Redirect to the specified URL 26 | window.location.replace(redirectURL); 27 | } 28 | }, [redirectURL]); 29 | 30 | // show a loading message if a redirect URL is present 31 | if (redirectURL) return ; 32 | 33 | // render children if no redirect URL is present 34 | return children; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/RedirectHandler/RedirectLoading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingCentered } from "components/Loading"; 2 | 3 | import { Container } from "../ui-base/Container/Container"; 4 | 5 | export function RedirectLoading() { 6 | return ( 7 | 15 | 16 |

Redirecting

17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/RedirectHandler/index.ts: -------------------------------------------------------------------------------- 1 | export { RedirectHandler } from "./RedirectHandler"; 2 | -------------------------------------------------------------------------------- /src/components/SuccessTextBox/SuccessTextBox.tsx: -------------------------------------------------------------------------------- 1 | import { SuccessCheckmarkIcon } from "assets/SuccessIcon"; 2 | 3 | import { Box } from "../ui-base/Box/Box"; 4 | import { Container } from "../ui-base/Container/Container"; 5 | 6 | interface ConnectedSuccessBoxProps { 7 | text: string; 8 | children?: React.ReactNode; 9 | } 10 | export function SuccessTextBox({ text, children }: ConnectedSuccessBoxProps) { 11 | return ( 12 | 13 | 22 | 23 |

{text}

24 | {children} 25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/auth/ApiKeyAuth/LandingContentProps.tsx: -------------------------------------------------------------------------------- 1 | import { ProviderInfo } from "@generated/api/src"; 2 | 3 | import { ProviderMetadata } from "../providerMetadata"; 4 | 5 | export type LandingContentProps = { 6 | provider: string; 7 | providerInfo: ProviderInfo; 8 | handleSubmit: (form: IFormType) => void; 9 | error: string | null; 10 | isButtonDisabled?: boolean; 11 | }; 12 | 13 | export interface IFormType { 14 | apiKey: string; 15 | providerMetadata?: ProviderMetadata; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/auth/AuthCardLayoutTemplate.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "src/components/ui-base/Button"; 2 | import { 3 | AuthCardLayout, 4 | AuthTitle, 5 | } from "src/layout/AuthCardLayout/AuthCardLayout"; 6 | 7 | import { AuthErrorAlert } from "./AuthErrorAlert/AuthErrorAlert"; 8 | 9 | type AuthCardLayoutTemplateProps = { 10 | handleSubmit: (form: any) => void; 11 | error: string | null; 12 | isButtonDisabled?: boolean; 13 | providerName?: string; 14 | title?: string; 15 | children?: React.ReactNode; 16 | }; 17 | 18 | /** 19 | * Template that assembles AuthCardLayout, AuthTitle, AuthErrorAlert, a Button 20 | * with option to pass children and title 21 | * @param param0 22 | * @returns 23 | */ 24 | export function AuthCardLayoutTemplate({ 25 | handleSubmit, 26 | error, 27 | isButtonDisabled, 28 | providerName, 29 | children, 30 | title, 31 | }: AuthCardLayoutTemplateProps) { 32 | if (!title && !providerName) { 33 | throw new Error("Either title or providerName is required"); 34 | } 35 | 36 | return ( 37 | 38 | {title || `Set up ${providerName} integration`} 39 | 40 | {children} 41 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/components/auth/AuthErrorAlert/AuthErrorAlert.tsx: -------------------------------------------------------------------------------- 1 | import { FormErrorBox } from "src/components/FormErrorBox"; 2 | 3 | interface OAuthErrorAlertProps { 4 | error: string | null; 5 | } 6 | 7 | export function AuthErrorAlert({ error }: OAuthErrorAlertProps) { 8 | if (error) { 9 | return {error}; 10 | } 11 | 12 | return null; 13 | } 14 | -------------------------------------------------------------------------------- /src/components/auth/BasicAuth/BasicAuthFlow.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { GenerateConnectionOperationRequest } from "@generated/api/src"; 3 | import { useProject } from "context/ProjectContextProvider"; 4 | 5 | import { useCreateConnectionMutation } from "../useCreateConnectionMutation"; 6 | 7 | import { BasicAuthContent } from "./BasicAuthContent"; 8 | import { BasicAuthFlowProps } from "./BasicAuthFlowProps"; 9 | import { BasicCreds } from "./LandingContentProps"; 10 | 11 | export function BasicAuthFlow({ 12 | provider, 13 | providerInfo, 14 | consumerRef, 15 | consumerName, 16 | groupRef, 17 | groupName, 18 | children, 19 | selectedConnection, 20 | }: BasicAuthFlowProps) { 21 | const { projectIdOrName } = useProject(); 22 | const createConnectionMutation = useCreateConnectionMutation(); 23 | 24 | const onNext = useCallback( 25 | (form: BasicCreds) => { 26 | const { user, pass, providerMetadata } = form; 27 | 28 | const req: GenerateConnectionOperationRequest = { 29 | projectIdOrName, 30 | generateConnectionParams: { 31 | providerWorkspaceRef: providerMetadata?.workspace?.value, 32 | groupName, 33 | groupRef, 34 | consumerName, 35 | consumerRef, 36 | provider, 37 | basicAuth: { 38 | username: user, 39 | password: pass, 40 | }, 41 | ...(providerMetadata && { providerMetadata: providerMetadata }), 42 | }, 43 | }; 44 | createConnectionMutation.mutate(req); 45 | }, 46 | [ 47 | projectIdOrName, 48 | groupName, 49 | groupRef, 50 | consumerName, 51 | consumerRef, 52 | provider, 53 | createConnectionMutation, 54 | ], 55 | ); 56 | 57 | if (selectedConnection === null) { 58 | return ( 59 | 65 | ); 66 | } 67 | 68 | return children; 69 | } 70 | -------------------------------------------------------------------------------- /src/components/auth/BasicAuth/BasicAuthFlowProps.tsx: -------------------------------------------------------------------------------- 1 | import { Connection, ProviderInfo } from "@generated/api/src"; 2 | 3 | export type BasicAuthFlowProps = { 4 | provider: string; 5 | providerInfo: ProviderInfo; 6 | consumerRef: string; 7 | consumerName?: string; 8 | groupRef: string; 9 | groupName?: string; 10 | children: JSX.Element; 11 | selectedConnection: Connection | null; 12 | setSelectedConnection: (connection: Connection | null) => void; 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/auth/BasicAuth/LandingContentProps.tsx: -------------------------------------------------------------------------------- 1 | import { ProviderInfo } from "@generated/api/src"; 2 | 3 | import { ProviderMetadata } from "../providerMetadata"; 4 | 5 | export type BasicCreds = { 6 | user: string; 7 | pass: string; 8 | providerMetadata?: ProviderMetadata; 9 | }; 10 | 11 | export type LandingContentProps = { 12 | provider: string; 13 | providerInfo: ProviderInfo; 14 | handleSubmit: (form: BasicCreds) => void; 15 | error: string | null; 16 | isButtonDisabled?: boolean; 17 | }; 18 | -------------------------------------------------------------------------------- /src/components/auth/NoAuth/LandingContentProps.tsx: -------------------------------------------------------------------------------- 1 | export type LandingContentProps = { 2 | handleSubmit: () => void; 3 | error: string | null; 4 | isButtonDisabled?: boolean; 5 | providerName?: string; 6 | }; 7 | -------------------------------------------------------------------------------- /src/components/auth/NoAuth/NoAuthContent.tsx: -------------------------------------------------------------------------------- 1 | import { AuthCardLayoutTemplate } from "components/auth/AuthCardLayoutTemplate"; 2 | 3 | import { LandingContentProps } from "./LandingContentProps"; 4 | 5 | /** 6 | * This flow is only used as for a mock provider. This flow is used for testing only. 7 | * @param param0 8 | * @returns 9 | */ 10 | export function NoAuthContent({ ...props }: LandingContentProps) { 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/auth/NoAuth/NoAuthFlow.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { GenerateConnectionOperationRequest } from "@generated/api/src"; 3 | import { useProject } from "context/ProjectContextProvider"; 4 | import { Connection } from "services/api"; 5 | 6 | import { useCreateConnectionMutation } from "../useCreateConnectionMutation"; 7 | 8 | import { NoAuthContent } from "./NoAuthContent"; 9 | 10 | type NoAuthFlowProps = { 11 | provider: string; 12 | consumerRef: string; 13 | consumerName?: string; 14 | groupRef: string; 15 | groupName?: string; 16 | providerName?: string; 17 | children: JSX.Element; 18 | selectedConnection: Connection | null; 19 | }; 20 | 21 | /** 22 | * This flow is only used as for a mock provider. This flow is used for testing only. 23 | * @param param0 24 | * @returns 25 | */ 26 | export function NoAuthFlow({ 27 | provider, 28 | consumerRef, 29 | consumerName, 30 | groupRef, 31 | groupName, 32 | children, 33 | selectedConnection, 34 | providerName, 35 | }: NoAuthFlowProps) { 36 | const { projectIdOrName } = useProject(); 37 | const createConnectionMutation = useCreateConnectionMutation(); 38 | 39 | const onNext = useCallback(() => { 40 | const req: GenerateConnectionOperationRequest = { 41 | projectIdOrName, 42 | generateConnectionParams: { 43 | groupName, 44 | groupRef, 45 | consumerName, 46 | consumerRef, 47 | provider, 48 | }, 49 | }; 50 | createConnectionMutation.mutate(req); 51 | }, [ 52 | projectIdOrName, 53 | groupName, 54 | groupRef, 55 | consumerName, 56 | consumerRef, 57 | provider, 58 | createConnectionMutation, 59 | ]); 60 | 61 | if (selectedConnection === null) { 62 | return ( 63 | 68 | ); 69 | } 70 | 71 | return children; 72 | } 73 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/NoWorkspaceEntry/LandingContentProps.tsx: -------------------------------------------------------------------------------- 1 | export type LandingContentProps = { 2 | handleSubmit: () => void; 3 | error: string | null; 4 | isButtonDisabled?: boolean; 5 | providerName?: string; 6 | }; 7 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/NoWorkspaceEntry/NoWorkspaceEntryContent.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AuthCardLayout, 3 | AuthDescription, 4 | AuthTitle, 5 | } from "src/layout/AuthCardLayout/AuthCardLayout"; 6 | 7 | import { AuthErrorAlert } from "components/auth/AuthErrorAlert/AuthErrorAlert"; 8 | import { Button } from "components/ui-base/Button"; 9 | 10 | import { LandingContentProps } from "./LandingContentProps"; 11 | 12 | export function NoWorkspaceEntryContent({ 13 | handleSubmit, 14 | error, 15 | isButtonDisabled, 16 | providerName, 17 | }: LandingContentProps) { 18 | return ( 19 | 20 | {`Set up ${providerName} integration`} 21 | 22 | {`Click Next to sign into the ${providerName} account you'd like to sync.`} 23 | 24 | 25 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/WorkspaceEntry/Salesforce/SalesforceSubdomainEntry.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AuthCardLayout, 3 | AuthTitle, 4 | } from "src/layout/AuthCardLayout/AuthCardLayout"; 5 | 6 | import { AuthErrorAlert } from "components/auth/AuthErrorAlert/AuthErrorAlert"; 7 | import { FormComponent } from "components/form"; 8 | import { AccessibleLink } from "components/ui-base/AccessibleLink"; 9 | import { Button } from "components/ui-base/Button"; 10 | 11 | import { 12 | SALESFORCE_HELP_URL, 13 | SubdomainEntryProps, 14 | } from "./SubdomainEntryProps"; 15 | 16 | export function SalesforceSubdomainEntry({ 17 | handleSubmit, 18 | setWorkspace, 19 | error, 20 | isButtonDisabled, 21 | }: SubdomainEntryProps) { 22 | return ( 23 | 24 | Enter your Salesforce subdomain 25 | 26 | What is my Salesforce subdomain? 27 | 28 | 29 |
30 | setWorkspace(event.currentTarget.value)} 35 | /> 36 |

37 | .my.salesforce.com 38 |

39 |
40 | 48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/WorkspaceEntry/Salesforce/SubdomainEntryProps.tsx: -------------------------------------------------------------------------------- 1 | export const SALESFORCE_HELP_URL = 2 | "https://help.salesforce.com/s/articleView?id=sf.faq_domain_name_what.htm&type=5"; 3 | 4 | export type SubdomainEntryProps = { 5 | handleSubmit: () => void; 6 | setWorkspace: (workspace: string) => void; 7 | error: string | null; 8 | isButtonDisabled?: boolean; 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/WorkspaceEntry/WorkspaceEntryContent.tsx: -------------------------------------------------------------------------------- 1 | import { MetadataItemInput } from "@generated/api/src"; 2 | import { 3 | AuthCardLayout, 4 | AuthTitle, 5 | } from "src/layout/AuthCardLayout/AuthCardLayout"; 6 | 7 | import { AuthErrorAlert } from "components/auth/AuthErrorAlert/AuthErrorAlert"; 8 | import { FormComponent } from "components/form"; 9 | import { Button } from "components/ui-base/Button"; 10 | 11 | import { WorkspaceEntryProps } from "./WorkspaceEntryProps"; 12 | 13 | /** 14 | * 15 | * @param param0 16 | * @returns 17 | */ 18 | export function WorkspaceEntryContent({ 19 | handleSubmit, 20 | setFormData, 21 | error, 22 | isButtonDisabled, 23 | providerName, 24 | metadataFields, 25 | }: WorkspaceEntryProps) { 26 | return ( 27 | 28 | Enter your {providerName} workspace 29 | 30 |
31 | {metadataFields.map((metadata: MetadataItemInput) => ( 32 | 39 | setFormData(metadata.name, event.currentTarget.value) 40 | } 41 | /> 42 | ))} 43 |
44 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/WorkspaceEntry/WorkspaceEntryProps.tsx: -------------------------------------------------------------------------------- 1 | import { MetadataItemInput } from "@generated/api/src"; 2 | 3 | export type WorkspaceEntryProps = { 4 | handleSubmit: () => void; 5 | setFormData: (metadata: string, value: string) => void; 6 | error: string | null; 7 | isButtonDisabled?: boolean; 8 | providerName?: string; 9 | metadataFields: MetadataItemInput[]; 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/AuthorizationCode/enableCSRFprotection.ts: -------------------------------------------------------------------------------- 1 | import { getEnvVariable } from "src/utils"; 2 | 3 | const VITE_ENABLE_CSRF = getEnvVariable("VITE_AMP_ENABLE_CSRF", false); 4 | const NEXT_ENABLE_CSRF = getEnvVariable("NEXT_AMP_ENABLE_CSRF", false); 5 | const REACT_APP_ENABLE_CSRF = getEnvVariable( 6 | "REACT_APP_AMP_ENABLE_CSRF", 7 | false, 8 | ); 9 | export const enableCSRFProtection = 10 | !!VITE_ENABLE_CSRF || !!NEXT_ENABLE_CSRF || !!REACT_APP_ENABLE_CSRF; 11 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/ClientCredentials/ClientCredentials.tsx: -------------------------------------------------------------------------------- 1 | import { Connection } from "@generated/api/src"; 2 | 3 | import { ClientCredsContainer } from "./ClientCredentialsContainer"; 4 | 5 | interface ClientCredsFlowProps { 6 | provider: string; 7 | consumerRef: string; 8 | consumerName?: string; 9 | groupRef: string; 10 | groupName?: string; 11 | providerName?: string; 12 | explicitScopesRequired?: boolean; 13 | selectedConnection: Connection | null; 14 | setSelectedConnection: (connection: Connection | null) => void; 15 | } 16 | 17 | export function ClientCredentials({ ...props }: ClientCredsFlowProps) { 18 | return ; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/auth/Oauth/ClientCredentials/ClientCredentialsCredsContent.tsx: -------------------------------------------------------------------------------- 1 | import { ProviderMetadataInfo } from "@generated/api/src"; 2 | 3 | export type ClientCredentialsCredsContent = { 4 | clientId: string; 5 | clientSecret: string; 6 | scopes?: string[]; 7 | providerMetadata?: Record; 8 | }; 9 | -------------------------------------------------------------------------------- /src/components/auth/providerMetadata.ts: -------------------------------------------------------------------------------- 1 | import { MetadataItemInput, ProviderMetadataInfo } from "@generated/api/src"; 2 | 3 | export type ProviderMetadata = Record; 4 | 5 | /** 6 | * Get the required metadata for the provider from the form data. 7 | * @param metadataFields - The metadata required by the provider. Is defined in the provider info. 8 | * @param formData - The form data to get the metadata from. 9 | * @returns Metadata that can be sent over the API. 10 | */ 11 | export function getProviderMetadata( 12 | metadataFields: MetadataItemInput[], 13 | formData: Record, 14 | ): ProviderMetadata { 15 | const metadata: ProviderMetadata = {}; 16 | const missingFields: string[] = []; 17 | 18 | metadataFields.forEach((item: MetadataItemInput) => { 19 | const value = formData[item.name]; 20 | if (!value || value.trim() === "") { 21 | missingFields.push(item.displayName || item.name); 22 | } else { 23 | metadata[item.name] = { 24 | value, 25 | source: "input", 26 | }; 27 | } 28 | }); 29 | 30 | if (missingFields.length > 0) { 31 | throw new Error( 32 | `Please fill in the following required fields: ${missingFields.join(", ")}.`, 33 | ); 34 | } 35 | 36 | return metadata; 37 | } 38 | 39 | /** 40 | * Check if the required metadata for the provider has been filled in. 41 | * This will be more useful when we have required/optional metadata later on. 42 | * @param metadataFields - The metadata required by the provider. Is defined in the provider info. 43 | * @param formData - The form data to check the metadata against. 44 | * @returns True if all required metadata has been filled in, false otherwise. 45 | */ 46 | export function isProviderMetadataValid( 47 | metadataFields: MetadataItemInput[], 48 | formData: Record, 49 | ): boolean { 50 | return metadataFields.every((item: MetadataItemInput) => { 51 | const value = formData[item.name]; 52 | return value && value.trim() !== ""; 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /src/components/auth/useCreateConnectionMutation.ts: -------------------------------------------------------------------------------- 1 | import { GenerateConnectionOperationRequest } from "@generated/api/src"; 2 | import { useMutation, useQueryClient } from "@tanstack/react-query"; 3 | import { useAPI } from "services/api"; 4 | import { handleServerError } from "src/utils/handleServerError"; 5 | 6 | export function useCreateConnectionMutation() { 7 | const getAPI = useAPI(); 8 | const queryClient = useQueryClient(); 9 | 10 | return useMutation({ 11 | mutationKey: ["createConnection"], 12 | mutationFn: async (params: GenerateConnectionOperationRequest) => { 13 | const api = await getAPI(); 14 | return api.connectionApi.generateConnection(params); 15 | }, 16 | onSuccess: () => { 17 | queryClient.invalidateQueries({ queryKey: ["amp", "connections"] }); 18 | }, 19 | onError: (error) => { 20 | console.error("Error creating connection and loading provider info"); 21 | handleServerError(error); 22 | }, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/form/FormControl/formControl.module.css: -------------------------------------------------------------------------------- 1 | .formControl { 2 | margin-bottom: .25rem; 3 | } 4 | 5 | .formLabel { 6 | font-weight: bold; 7 | margin-bottom: 0.5rem; 8 | display: block; 9 | } 10 | 11 | .formLabelRequired::after { 12 | content: ' *'; 13 | color: var(--amp-colors-status-critical-dark); 14 | } 15 | 16 | .formInput { 17 | position: relative; 18 | } 19 | 20 | .formInputInvalid input { 21 | border-color: var(--amp-colors-status-critical); 22 | } 23 | 24 | .formError { 25 | color: var(--amp-colors-status-critical-dark); 26 | font-size: 0.875rem; 27 | margin-top: 0.5rem; 28 | } 29 | 30 | .disabled { 31 | opacity: 0.6; 32 | pointer-events: none; 33 | } 34 | -------------------------------------------------------------------------------- /src/components/form/FormControl/index.tsx: -------------------------------------------------------------------------------- 1 | // FormControl.tsx 2 | import React from "react"; 3 | import classNames from "classnames"; 4 | 5 | import styles from "./formControl.module.css"; 6 | 7 | type FormControlProps = { 8 | id: string; 9 | label?: string; 10 | isRequired?: boolean; 11 | isDisabled?: boolean; 12 | isInvalid?: boolean; 13 | errorMessage?: string; 14 | children: React.ReactNode; 15 | }; 16 | 17 | /** 18 | * mimics the FormControl component from chakra-ui 19 | * renders a form control with a label and error message 20 | * @param param0 21 | * @returns 22 | */ 23 | export function FormControl({ 24 | id, 25 | label, 26 | isRequired = false, 27 | isDisabled = false, 28 | isInvalid = false, 29 | errorMessage, 30 | children, 31 | }: FormControlProps) { 32 | return ( 33 |
38 | {label && ( 39 | 47 | )} 48 |
54 | {children} 55 |
56 | {isInvalid && errorMessage && ( 57 |
58 | {errorMessage} 59 |
60 | )} 61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/components/form/Input/index.tsx: -------------------------------------------------------------------------------- 1 | import { InputHTMLAttributes } from "react"; 2 | import classNames from "classnames"; 3 | 4 | import classes from "./input.module.css"; 5 | 6 | export interface InputProps extends InputHTMLAttributes { 7 | id: string; 8 | type: string; 9 | className?: string; 10 | isError?: boolean; 11 | } 12 | 13 | export function Input({ 14 | id, 15 | type, 16 | className, 17 | isError = false, 18 | ...rest 19 | }: InputProps) { 20 | const defaultClassName = isError 21 | ? classNames(classes.inputError, classes.input) 22 | : classes.input; 23 | 24 | return ( 25 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/form/Input/input.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | border-radius: var(--amp-default-border-radius); 3 | border: 1px solid; 4 | border-color: var(--amp-colors-input-border-default); 5 | height: 2.5rem; 6 | transition: all .20s ease-in; 7 | 8 | color: var(--amp-colors-input-content-default); 9 | background-color: var(--amp-colors-input-surface-default); 10 | font-size: 1rem; 11 | font-weight: 400; 12 | line-height: 1.5rem; /* 150% */ 13 | padding: 0 1rem; 14 | width: 100%; 15 | transition: all .1s ease; 16 | } 17 | 18 | .input:hover { 19 | border-color: var(--amp-colors-input-border-hover); 20 | background-color: var(--amp-colors-input-surface-hover); 21 | } 22 | 23 | .input::placeholder { 24 | color: var(--amp-colors-input-content-placeholder-text); 25 | } 26 | 27 | .inputError { 28 | border: 1px solid var(--amp-colors-status-critical); 29 | } 30 | 31 | .input:focus:enabled { 32 | border: 1px solid var(--amp-colors-input-border-focus); 33 | outline: none; 34 | background-color: var(--amp-colors-input-surface-focus); 35 | } 36 | 37 | .input:disabled { 38 | background-color: var(--amp-colors-input-surface-disabled); 39 | border: 1px solid var(--amp-colors-input-border-disabled); 40 | color: var(--amp-colors-input-content-disabled) ; 41 | } 42 | 43 | .error { 44 | color: var(--amp-colors-status-critical); 45 | font-size: 0.875rem; 46 | font-weight: 400; 47 | line-height: 1.25rem; /* 142.857% */ 48 | } 49 | -------------------------------------------------------------------------------- /src/components/form/PasswordInput/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { PasswordEyeIcon } from "src/assets/PasswordEyeIcon"; 3 | import { PasswordEyeSlashIcon } from "src/assets/PasswordEyeSlashIcon"; 4 | import { FormComponent } from "src/components/form"; 5 | import { Button } from "src/components/ui-base/Button"; 6 | 7 | export interface PasswordInputProps { 8 | id: string; 9 | name: string; 10 | placeholder?: string; 11 | onChange: (event: React.FormEvent) => void; 12 | value?: string; 13 | className?: string; 14 | isError?: boolean; 15 | } 16 | 17 | export function PasswordInput({ 18 | id, 19 | name, 20 | placeholder = "Password", 21 | onChange, 22 | value, 23 | className, 24 | isError, 25 | }: PasswordInputProps) { 26 | const [show, setShow] = useState(false); 27 | const onToggleShowHide = () => setShow((prevShow) => !prevShow); 28 | 29 | return ( 30 |
31 | 41 | 58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/form/Textarea/index.tsx: -------------------------------------------------------------------------------- 1 | import { TextareaHTMLAttributes } from "react"; 2 | import classNames from "classnames"; 3 | 4 | import classes from "./textarea.module.css"; 5 | 6 | export interface TextareaProps 7 | extends TextareaHTMLAttributes { 8 | className?: string; 9 | isError?: boolean; 10 | } 11 | 12 | export function Textarea({ 13 | className, 14 | isError = false, 15 | ...rest 16 | }: TextareaProps) { 17 | const defaultClassName = isError 18 | ? classNames(classes.textareaError, classes.textarea) 19 | : classes.textarea; 20 | 21 | return ( 22 |