From 73f844d544e2f859ea422b207796807a77947def Mon Sep 17 00:00:00 2001 From: Jan Baykara Date: Wed, 29 Jan 2025 22:50:02 +0000 Subject: [PATCH] WIP new data source setup screen --- nextjs/package-lock.json | 22 + nextjs/package.json | 2 + nextjs/src/__generated__/gql.ts | 30 - nextjs/src/__generated__/graphql.ts | 49 - .../configure/[externalDataSourceId]/page.tsx | 197 --- .../[externalDataSourceType]/oauthQueries.ts | 19 - .../connect/[externalDataSourceType]/page.tsx | 1432 ----------------- .../(logged-in)/data-sources/create/page.tsx | 64 - .../review/[externalDataProviderId]/page.tsx | 167 -- .../create/NewExternalDataSourceWrapper.tsx | 0 .../data-sources/create/layout.tsx | 8 +- .../data-sources/create/page.tsx | 7 + .../graphiql/layout.tsx | 0 .../graphiql/page.tsx | 0 .../src/app/(logged-in-fullscreen)/layout.tsx | 23 + .../configure/CreateDataSourceFlow.tsx | 43 + .../data-sources/configure/Geocoding.tsx | 7 + .../data-sources/configure/Ingest.tsx | 40 + .../data-sources/configure/Mapping.tsx | 7 + .../data-sources/configure/Metadata.tsx | 7 + .../data-sources/configure/Review.tsx | 7 + .../data-sources/configure/screens.ts | 34 + .../data-sources/configure/state.ts | 77 + nextjs/src/lib/data.ts | 2 +- nextjs/tsconfig.json | 2 + 25 files changed, 281 insertions(+), 1965 deletions(-) delete mode 100644 nextjs/src/app/(logged-in)/data-sources/create/configure/[externalDataSourceId]/page.tsx delete mode 100644 nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/oauthQueries.ts delete mode 100644 nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/page.tsx delete mode 100644 nextjs/src/app/(logged-in)/data-sources/create/page.tsx delete mode 100644 nextjs/src/app/(logged-in)/data-sources/create/review/[externalDataProviderId]/page.tsx rename nextjs/src/app/{(logged-in) => (logged-in-fullscreen)}/data-sources/create/NewExternalDataSourceWrapper.tsx (100%) rename nextjs/src/app/{(logged-in) => (logged-in-fullscreen)}/data-sources/create/layout.tsx (62%) create mode 100644 nextjs/src/app/(logged-in-fullscreen)/data-sources/create/page.tsx rename nextjs/src/app/{(logged-in) => (logged-in-fullscreen)}/graphiql/layout.tsx (100%) rename nextjs/src/app/{(logged-in) => (logged-in-fullscreen)}/graphiql/page.tsx (100%) create mode 100644 nextjs/src/app/(logged-in-fullscreen)/layout.tsx create mode 100644 nextjs/src/components/data-sources/configure/CreateDataSourceFlow.tsx create mode 100644 nextjs/src/components/data-sources/configure/Geocoding.tsx create mode 100644 nextjs/src/components/data-sources/configure/Ingest.tsx create mode 100644 nextjs/src/components/data-sources/configure/Mapping.tsx create mode 100644 nextjs/src/components/data-sources/configure/Metadata.tsx create mode 100644 nextjs/src/components/data-sources/configure/Review.tsx create mode 100644 nextjs/src/components/data-sources/configure/screens.ts create mode 100644 nextjs/src/components/data-sources/configure/state.ts diff --git a/nextjs/package-lock.json b/nextjs/package-lock.json index fa06c0d78..6bd816cec 100644 --- a/nextjs/package-lock.json +++ b/nextjs/package-lock.json @@ -68,6 +68,7 @@ "jotai": "^2.7.1", "jotai-effect": "^0.6.0", "jotai-location": "^0.5.4", + "jotai-xstate": "^0.6.1", "js-cookie": "^3.0.5", "keyboard-key": "^1.1.0", "lodash": "^4.17.21", @@ -105,6 +106,7 @@ "update": "^0.7.4", "uuid": "^11.0.3", "vanilla-cookieconsent": "^3.0.1", + "xstate": "^5.19.2", "zod": "^3.22.4" }, "devDependencies": { @@ -26668,6 +26670,16 @@ "jotai": ">=1.11.0" } }, + "node_modules/jotai-xstate": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/jotai-xstate/-/jotai-xstate-0.6.1.tgz", + "integrity": "sha512-graSDsorui1a4Zt4jCJ40GuOzOkfOY317XcazYnNx0EHS+D/rOr4aLzZRy0NLaPQZsypfcGSMbNCwksfhwkixA==", + "license": "MIT", + "peerDependencies": { + "jotai": ">=2.0.0", + "xstate": ">=5.0.0" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -37468,6 +37480,16 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, + "node_modules/xstate": { + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.19.2.tgz", + "integrity": "sha512-B8fL2aP0ogn5aviAXFzI5oZseAMqN00fg/TeDa3ZtatyDcViYLIfuQl4y8qmHCiKZgGEzmnTyNtNQL9oeJE2gw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/xstate" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/nextjs/package.json b/nextjs/package.json index e9862321d..41d6a8a2e 100644 --- a/nextjs/package.json +++ b/nextjs/package.json @@ -84,6 +84,7 @@ "jotai": "^2.7.1", "jotai-effect": "^0.6.0", "jotai-location": "^0.5.4", + "jotai-xstate": "^0.6.1", "js-cookie": "^3.0.5", "keyboard-key": "^1.1.0", "lodash": "^4.17.21", @@ -121,6 +122,7 @@ "update": "^0.7.4", "uuid": "^11.0.3", "vanilla-cookieconsent": "^3.0.1", + "xstate": "^5.19.2", "zod": "^3.22.4" }, "devDependencies": { diff --git a/nextjs/src/__generated__/gql.ts b/nextjs/src/__generated__/gql.ts index 3a169721c..68f1f1cd0 100644 --- a/nextjs/src/__generated__/gql.ts +++ b/nextjs/src/__generated__/gql.ts @@ -23,12 +23,6 @@ const documents = { "\n mutation PerformPasswordReset(\n $token: String!\n $password1: String!\n $password2: String!\n ) {\n performPasswordReset(\n token: $token\n newPassword1: $password1\n newPassword2: $password2\n ) {\n errors\n success\n }\n }\n": types.PerformPasswordResetDocument, "\n mutation ResetPassword($email: String!) {\n requestPasswordReset(email: $email) {\n errors\n success\n }\n }\n": types.ResetPasswordDocument, "\n query ListOrganisations($currentOrganisationId: ID!) {\n myOrganisations(filters: { id: $currentOrganisationId }) {\n id\n externalDataSources {\n id\n name\n dataType\n connectionDetails {\n ... on AirtableSource {\n baseId\n tableId\n }\n ... on MailchimpSource {\n apiKey\n listId\n }\n }\n crmType\n autoImportEnabled\n autoUpdateEnabled\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n sharingPermissions {\n id\n organisation {\n id\n name\n }\n }\n }\n sharingPermissionsFromOtherOrgs {\n id\n externalDataSource {\n id\n name\n dataType\n crmType\n organisation {\n name\n }\n }\n }\n }\n }\n": types.ListOrganisationsDocument, - "\n query GetSourceMapping($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n autoImportEnabled\n autoUpdateEnabled\n allowUpdates\n hasWebhooks\n updateMapping {\n destinationColumn\n source\n sourcePath\n }\n fieldDefinitions {\n label\n value\n description\n editable\n }\n crmType\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n postcodeField\n firstNameField\n lastNameField\n emailField\n phoneField\n addressField\n canDisplayPointField\n }\n }\n": types.GetSourceMappingDocument, - "\n query GoogleSheetsOauthUrl($redirectUrl: String!) {\n googleSheetsOauthUrl(redirectUrl: $redirectUrl)\n }\n": types.GoogleSheetsOauthUrlDocument, - "\n query GoogleSheetsOauthCredentials(\n $redirectSuccessUrl: String!\n $externalDataSourceId: String\n ) {\n googleSheetsOauthCredentials(\n redirectSuccessUrl: $redirectSuccessUrl\n externalDataSourceId: $externalDataSourceId\n )\n }\n": types.GoogleSheetsOauthCredentialsDocument, - "\n query TestDataSource($input: CreateExternalDataSourceInput!) {\n testDataSource(input: $input) {\n __typename\n crmType\n fieldDefinitions {\n label\n value\n description\n editable\n }\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n healthcheck\n predefinedColumnNames\n defaultDataType\n remoteName\n allowUpdates\n defaults\n oauthCredentials\n }\n }\n": types.TestDataSourceDocument, - "\n mutation CreateSource($input: CreateExternalDataSourceInput!) {\n createExternalDataSource(input: $input) {\n code\n errors {\n message\n }\n result {\n id\n name\n crmType\n dataType\n allowUpdates\n }\n }\n }\n": types.CreateSourceDocument, - "\n query AutoUpdateCreationReview($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n name\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n dataType\n crmType\n autoImportEnabled\n autoUpdateEnabled\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n automatedWebhooks\n webhookUrl\n ...DataSourceCard\n }\n }\n \n": types.AutoUpdateCreationReviewDocument, "\n mutation UpdateExternalDataSourceApiKey($id: String!, $apiKey: String!) {\n updateExternalDataSourceApiKey(id: $id, apiKey: $apiKey)\n }\n": types.UpdateExternalDataSourceApiKeyDocument, "\n query ExternalDataSourceInspectPage($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n name\n dataType\n remoteUrl\n crmType\n connectionDetails {\n ... on AirtableSource {\n apiKey\n baseId\n tableId\n }\n ... on MailchimpSource {\n apiKey\n listId\n }\n ... on ActionNetworkSource {\n apiKey\n groupSlug\n }\n ... on TicketTailorSource {\n apiKey\n }\n }\n lastImportJob {\n id\n lastEventAt\n status\n }\n lastUpdateJob {\n id\n lastEventAt\n status\n }\n autoImportEnabled\n autoUpdateEnabled\n hasWebhooks\n allowUpdates\n automatedWebhooks\n webhookUrl\n webhookHealthcheck\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n postcodeField\n firstNameField\n lastNameField\n fullNameField\n emailField\n phoneField\n addressField\n titleField\n descriptionField\n imageField\n startTimeField\n endTimeField\n publicUrlField\n socialUrlField\n canDisplayPointField\n isImportScheduled\n importProgress {\n id\n hasForecast\n status\n total\n succeeded\n estimatedFinishTime\n actualFinishTime\n inQueue\n numberOfJobsAheadInQueue\n sendEmail\n }\n isUpdateScheduled\n updateProgress {\n id\n hasForecast\n status\n total\n succeeded\n estimatedFinishTime\n actualFinishTime\n inQueue\n numberOfJobsAheadInQueue\n sendEmail\n }\n importedDataCount\n importedDataGeocodingRate\n regionCount: importedDataCountOfAreas(\n analyticalAreaType: european_electoral_region\n )\n constituencyCount: importedDataCountOfAreas(\n analyticalAreaType: parliamentary_constituency\n )\n ladCount: importedDataCountOfAreas(analyticalAreaType: admin_district)\n wardCount: importedDataCountOfAreas(analyticalAreaType: admin_ward)\n fieldDefinitions {\n label\n value\n description\n editable\n }\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n sharingPermissions {\n id\n }\n organisation {\n id\n name\n }\n }\n }\n": types.ExternalDataSourceInspectPageDocument, "\n mutation DeleteUpdateConfig($id: String!) {\n deleteExternalDataSource(data: { id: $id }) {\n id\n }\n }\n": types.DeleteUpdateConfigDocument, @@ -146,30 +140,6 @@ export function gql(source: "\n mutation ResetPassword($email: String!) {\n * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function gql(source: "\n query ListOrganisations($currentOrganisationId: ID!) {\n myOrganisations(filters: { id: $currentOrganisationId }) {\n id\n externalDataSources {\n id\n name\n dataType\n connectionDetails {\n ... on AirtableSource {\n baseId\n tableId\n }\n ... on MailchimpSource {\n apiKey\n listId\n }\n }\n crmType\n autoImportEnabled\n autoUpdateEnabled\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n sharingPermissions {\n id\n organisation {\n id\n name\n }\n }\n }\n sharingPermissionsFromOtherOrgs {\n id\n externalDataSource {\n id\n name\n dataType\n crmType\n organisation {\n name\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query ListOrganisations($currentOrganisationId: ID!) {\n myOrganisations(filters: { id: $currentOrganisationId }) {\n id\n externalDataSources {\n id\n name\n dataType\n connectionDetails {\n ... on AirtableSource {\n baseId\n tableId\n }\n ... on MailchimpSource {\n apiKey\n listId\n }\n }\n crmType\n autoImportEnabled\n autoUpdateEnabled\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n sharingPermissions {\n id\n organisation {\n id\n name\n }\n }\n }\n sharingPermissionsFromOtherOrgs {\n id\n externalDataSource {\n id\n name\n dataType\n crmType\n organisation {\n name\n }\n }\n }\n }\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n query GetSourceMapping($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n autoImportEnabled\n autoUpdateEnabled\n allowUpdates\n hasWebhooks\n updateMapping {\n destinationColumn\n source\n sourcePath\n }\n fieldDefinitions {\n label\n value\n description\n editable\n }\n crmType\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n postcodeField\n firstNameField\n lastNameField\n emailField\n phoneField\n addressField\n canDisplayPointField\n }\n }\n"): (typeof documents)["\n query GetSourceMapping($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n autoImportEnabled\n autoUpdateEnabled\n allowUpdates\n hasWebhooks\n updateMapping {\n destinationColumn\n source\n sourcePath\n }\n fieldDefinitions {\n label\n value\n description\n editable\n }\n crmType\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n postcodeField\n firstNameField\n lastNameField\n emailField\n phoneField\n addressField\n canDisplayPointField\n }\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n query GoogleSheetsOauthUrl($redirectUrl: String!) {\n googleSheetsOauthUrl(redirectUrl: $redirectUrl)\n }\n"): (typeof documents)["\n query GoogleSheetsOauthUrl($redirectUrl: String!) {\n googleSheetsOauthUrl(redirectUrl: $redirectUrl)\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n query GoogleSheetsOauthCredentials(\n $redirectSuccessUrl: String!\n $externalDataSourceId: String\n ) {\n googleSheetsOauthCredentials(\n redirectSuccessUrl: $redirectSuccessUrl\n externalDataSourceId: $externalDataSourceId\n )\n }\n"): (typeof documents)["\n query GoogleSheetsOauthCredentials(\n $redirectSuccessUrl: String!\n $externalDataSourceId: String\n ) {\n googleSheetsOauthCredentials(\n redirectSuccessUrl: $redirectSuccessUrl\n externalDataSourceId: $externalDataSourceId\n )\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n query TestDataSource($input: CreateExternalDataSourceInput!) {\n testDataSource(input: $input) {\n __typename\n crmType\n fieldDefinitions {\n label\n value\n description\n editable\n }\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n healthcheck\n predefinedColumnNames\n defaultDataType\n remoteName\n allowUpdates\n defaults\n oauthCredentials\n }\n }\n"): (typeof documents)["\n query TestDataSource($input: CreateExternalDataSourceInput!) {\n testDataSource(input: $input) {\n __typename\n crmType\n fieldDefinitions {\n label\n value\n description\n editable\n }\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n healthcheck\n predefinedColumnNames\n defaultDataType\n remoteName\n allowUpdates\n defaults\n oauthCredentials\n }\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n mutation CreateSource($input: CreateExternalDataSourceInput!) {\n createExternalDataSource(input: $input) {\n code\n errors {\n message\n }\n result {\n id\n name\n crmType\n dataType\n allowUpdates\n }\n }\n }\n"): (typeof documents)["\n mutation CreateSource($input: CreateExternalDataSourceInput!) {\n createExternalDataSource(input: $input) {\n code\n errors {\n message\n }\n result {\n id\n name\n crmType\n dataType\n allowUpdates\n }\n }\n }\n"]; -/** - * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function gql(source: "\n query AutoUpdateCreationReview($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n name\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n dataType\n crmType\n autoImportEnabled\n autoUpdateEnabled\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n automatedWebhooks\n webhookUrl\n ...DataSourceCard\n }\n }\n \n"): (typeof documents)["\n query AutoUpdateCreationReview($ID: ID!) {\n externalDataSource(pk: $ID) {\n id\n name\n geographyColumn\n geographyColumnType\n geocodingConfig\n usesValidGeocodingConfig\n dataType\n crmType\n autoImportEnabled\n autoUpdateEnabled\n updateMapping {\n source\n sourcePath\n destinationColumn\n }\n jobs(pagination: { limit: 10 }) {\n lastEventAt\n status\n }\n automatedWebhooks\n webhookUrl\n ...DataSourceCard\n }\n }\n \n"]; /** * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/nextjs/src/__generated__/graphql.ts b/nextjs/src/__generated__/graphql.ts index 2f7cf6e09..a3210c080 100644 --- a/nextjs/src/__generated__/graphql.ts +++ b/nextjs/src/__generated__/graphql.ts @@ -3230,49 +3230,6 @@ export type ListOrganisationsQueryVariables = Exact<{ export type ListOrganisationsQuery = { __typename?: 'Query', myOrganisations: Array<{ __typename?: 'Organisation', id: string, externalDataSources: Array<{ __typename?: 'ExternalDataSource', id: any, name: string, dataType: DataSourceType, crmType: CrmType, autoImportEnabled: boolean, autoUpdateEnabled: boolean, connectionDetails: { __typename?: 'ActionNetworkSource' } | { __typename?: 'AirtableSource', baseId: string, tableId: string } | { __typename?: 'EditableGoogleSheetsSource' } | { __typename?: 'MailchimpSource', apiKey: string, listId: string } | { __typename?: 'TicketTailorSource' }, jobs: Array<{ __typename?: 'QueueJob', lastEventAt: any, status: ProcrastinateJobStatus }>, updateMapping?: Array<{ __typename?: 'AutoUpdateConfig', source: string, sourcePath: string, destinationColumn: string }> | null, sharingPermissions: Array<{ __typename?: 'SharingPermission', id: any, organisation: { __typename?: 'PublicOrganisation', id: string, name: string } }> }>, sharingPermissionsFromOtherOrgs: Array<{ __typename?: 'SharingPermission', id: any, externalDataSource: { __typename?: 'SharedDataSource', id: any, name: string, dataType: DataSourceType, crmType: CrmType, organisation: { __typename?: 'PublicOrganisation', name: string } } }> }> }; -export type GetSourceMappingQueryVariables = Exact<{ - ID: Scalars['ID']['input']; -}>; - - -export type GetSourceMappingQuery = { __typename?: 'Query', externalDataSource: { __typename?: 'ExternalDataSource', id: any, autoImportEnabled: boolean, autoUpdateEnabled: boolean, allowUpdates: boolean, hasWebhooks: boolean, crmType: CrmType, geographyColumn?: string | null, geographyColumnType: GeographyTypes, geocodingConfig: any, usesValidGeocodingConfig: boolean, postcodeField?: string | null, firstNameField?: string | null, lastNameField?: string | null, emailField?: string | null, phoneField?: string | null, addressField?: string | null, canDisplayPointField?: string | null, updateMapping?: Array<{ __typename?: 'AutoUpdateConfig', destinationColumn: string, source: string, sourcePath: string }> | null, fieldDefinitions?: Array<{ __typename?: 'FieldDefinition', label?: string | null, value: string, description?: string | null, editable: boolean }> | null } }; - -export type GoogleSheetsOauthUrlQueryVariables = Exact<{ - redirectUrl: Scalars['String']['input']; -}>; - - -export type GoogleSheetsOauthUrlQuery = { __typename?: 'Query', googleSheetsOauthUrl: string }; - -export type GoogleSheetsOauthCredentialsQueryVariables = Exact<{ - redirectSuccessUrl: Scalars['String']['input']; - externalDataSourceId?: InputMaybe; -}>; - - -export type GoogleSheetsOauthCredentialsQuery = { __typename?: 'Query', googleSheetsOauthCredentials: string }; - -export type TestDataSourceQueryVariables = Exact<{ - input: CreateExternalDataSourceInput; -}>; - - -export type TestDataSourceQuery = { __typename?: 'Query', testDataSource: { __typename: 'ExternalDataSource', crmType: CrmType, geographyColumn?: string | null, geographyColumnType: GeographyTypes, geocodingConfig: any, usesValidGeocodingConfig: boolean, healthcheck: boolean, predefinedColumnNames: boolean, defaultDataType?: string | null, remoteName?: string | null, allowUpdates: boolean, defaults: any, oauthCredentials?: string | null, fieldDefinitions?: Array<{ __typename?: 'FieldDefinition', label?: string | null, value: string, description?: string | null, editable: boolean }> | null } }; - -export type CreateSourceMutationVariables = Exact<{ - input: CreateExternalDataSourceInput; -}>; - - -export type CreateSourceMutation = { __typename?: 'Mutation', createExternalDataSource: { __typename?: 'CreateExternalDataSourceOutput', code: number, errors: Array<{ __typename?: 'MutationError', message: string }>, result?: { __typename?: 'ExternalDataSource', id: any, name: string, crmType: CrmType, dataType: DataSourceType, allowUpdates: boolean } | null } }; - -export type AutoUpdateCreationReviewQueryVariables = Exact<{ - ID: Scalars['ID']['input']; -}>; - - -export type AutoUpdateCreationReviewQuery = { __typename?: 'Query', externalDataSource: { __typename?: 'ExternalDataSource', id: any, name: string, geographyColumn?: string | null, geographyColumnType: GeographyTypes, geocodingConfig: any, usesValidGeocodingConfig: boolean, dataType: DataSourceType, crmType: CrmType, autoImportEnabled: boolean, autoUpdateEnabled: boolean, automatedWebhooks: boolean, webhookUrl: string, updateMapping?: Array<{ __typename?: 'AutoUpdateConfig', source: string, sourcePath: string, destinationColumn: string }> | null, jobs: Array<{ __typename?: 'QueueJob', lastEventAt: any, status: ProcrastinateJobStatus }>, sharingPermissions: Array<{ __typename?: 'SharingPermission', id: any, organisation: { __typename?: 'PublicOrganisation', id: string, name: string } }> } }; - export type UpdateExternalDataSourceApiKeyMutationVariables = Exact<{ id: Scalars['String']['input']; apiKey: Scalars['String']['input']; @@ -3733,12 +3690,6 @@ export const LoginDocument = {"kind":"Document","definitions":[{"kind":"Operatio export const PerformPasswordResetDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PerformPasswordReset"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password1"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password2"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performPasswordReset"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"newPassword1"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password1"}}},{"kind":"Argument","name":{"kind":"Name","value":"newPassword2"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password2"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errors"}},{"kind":"Field","name":{"kind":"Name","value":"success"}}]}}]}}]} as unknown as DocumentNode; export const ResetPasswordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResetPassword"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestPasswordReset"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errors"}},{"kind":"Field","name":{"kind":"Name","value":"success"}}]}}]}}]} as unknown as DocumentNode; export const ListOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListOrganisations"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"currentOrganisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"myOrganisations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"currentOrganisationId"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"externalDataSources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"connectionDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AirtableSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"baseId"}},{"kind":"Field","name":{"kind":"Name","value":"tableId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MailchimpSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}},{"kind":"Field","name":{"kind":"Name","value":"listId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"autoImportEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"autoUpdateEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"jobs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lastEventAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updateMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"destinationColumn"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sharingPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"sharingPermissionsFromOtherOrgs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"externalDataSource"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetSourceMappingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSourceMapping"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"externalDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pk"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"autoImportEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"autoUpdateEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"allowUpdates"}},{"kind":"Field","name":{"kind":"Name","value":"hasWebhooks"}},{"kind":"Field","name":{"kind":"Name","value":"updateMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"destinationColumn"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldDefinitions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"editable"}}]}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumn"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumnType"}},{"kind":"Field","name":{"kind":"Name","value":"geocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"usesValidGeocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"postcodeField"}},{"kind":"Field","name":{"kind":"Name","value":"firstNameField"}},{"kind":"Field","name":{"kind":"Name","value":"lastNameField"}},{"kind":"Field","name":{"kind":"Name","value":"emailField"}},{"kind":"Field","name":{"kind":"Name","value":"phoneField"}},{"kind":"Field","name":{"kind":"Name","value":"addressField"}},{"kind":"Field","name":{"kind":"Name","value":"canDisplayPointField"}}]}}]}}]} as unknown as DocumentNode; -export const GoogleSheetsOauthUrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GoogleSheetsOauthUrl"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"redirectUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"googleSheetsOauthUrl"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"redirectUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"redirectUrl"}}}]}]}}]} as unknown as DocumentNode; -export const GoogleSheetsOauthCredentialsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GoogleSheetsOauthCredentials"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"redirectSuccessUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"externalDataSourceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"googleSheetsOauthCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"redirectSuccessUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"redirectSuccessUrl"}}},{"kind":"Argument","name":{"kind":"Name","value":"externalDataSourceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"externalDataSourceId"}}}]}]}}]} as unknown as DocumentNode; -export const TestDataSourceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"TestDataSource"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateExternalDataSourceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"testDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDefinitions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"editable"}}]}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumn"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumnType"}},{"kind":"Field","name":{"kind":"Name","value":"geocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"usesValidGeocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"healthcheck"}},{"kind":"Field","name":{"kind":"Name","value":"predefinedColumnNames"}},{"kind":"Field","name":{"kind":"Name","value":"defaultDataType"}},{"kind":"Field","name":{"kind":"Name","value":"remoteName"}},{"kind":"Field","name":{"kind":"Name","value":"allowUpdates"}},{"kind":"Field","name":{"kind":"Name","value":"defaults"}},{"kind":"Field","name":{"kind":"Name","value":"oauthCredentials"}}]}}]}}]} as unknown as DocumentNode; -export const CreateSourceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSource"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateExternalDataSourceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createExternalDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}},{"kind":"Field","name":{"kind":"Name","value":"result"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"allowUpdates"}}]}}]}}]}}]} as unknown as DocumentNode; -export const AutoUpdateCreationReviewDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AutoUpdateCreationReview"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"externalDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pk"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumn"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumnType"}},{"kind":"Field","name":{"kind":"Name","value":"geocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"usesValidGeocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"autoImportEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"autoUpdateEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"updateMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"destinationColumn"}}]}},{"kind":"Field","name":{"kind":"Name","value":"jobs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lastEventAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"automatedWebhooks"}},{"kind":"Field","name":{"kind":"Name","value":"webhookUrl"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataSourceCard"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataSourceCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ExternalDataSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"automatedWebhooks"}},{"kind":"Field","name":{"kind":"Name","value":"autoImportEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"autoUpdateEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"updateMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"destinationColumn"}}]}},{"kind":"Field","name":{"kind":"Name","value":"jobs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lastEventAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sharingPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateExternalDataSourceApiKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateExternalDataSourceApiKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateExternalDataSourceApiKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"apiKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}}}]}]}}]} as unknown as DocumentNode; export const ExternalDataSourceInspectPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExternalDataSourceInspectPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"externalDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pk"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}},{"kind":"Field","name":{"kind":"Name","value":"remoteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"crmType"}},{"kind":"Field","name":{"kind":"Name","value":"connectionDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AirtableSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}},{"kind":"Field","name":{"kind":"Name","value":"baseId"}},{"kind":"Field","name":{"kind":"Name","value":"tableId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MailchimpSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}},{"kind":"Field","name":{"kind":"Name","value":"listId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ActionNetworkSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}},{"kind":"Field","name":{"kind":"Name","value":"groupSlug"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TicketTailorSource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastImportJob"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lastEventAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdateJob"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lastEventAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoImportEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"autoUpdateEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"hasWebhooks"}},{"kind":"Field","name":{"kind":"Name","value":"allowUpdates"}},{"kind":"Field","name":{"kind":"Name","value":"automatedWebhooks"}},{"kind":"Field","name":{"kind":"Name","value":"webhookUrl"}},{"kind":"Field","name":{"kind":"Name","value":"webhookHealthcheck"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumn"}},{"kind":"Field","name":{"kind":"Name","value":"geographyColumnType"}},{"kind":"Field","name":{"kind":"Name","value":"geocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"usesValidGeocodingConfig"}},{"kind":"Field","name":{"kind":"Name","value":"postcodeField"}},{"kind":"Field","name":{"kind":"Name","value":"firstNameField"}},{"kind":"Field","name":{"kind":"Name","value":"lastNameField"}},{"kind":"Field","name":{"kind":"Name","value":"fullNameField"}},{"kind":"Field","name":{"kind":"Name","value":"emailField"}},{"kind":"Field","name":{"kind":"Name","value":"phoneField"}},{"kind":"Field","name":{"kind":"Name","value":"addressField"}},{"kind":"Field","name":{"kind":"Name","value":"titleField"}},{"kind":"Field","name":{"kind":"Name","value":"descriptionField"}},{"kind":"Field","name":{"kind":"Name","value":"imageField"}},{"kind":"Field","name":{"kind":"Name","value":"startTimeField"}},{"kind":"Field","name":{"kind":"Name","value":"endTimeField"}},{"kind":"Field","name":{"kind":"Name","value":"publicUrlField"}},{"kind":"Field","name":{"kind":"Name","value":"socialUrlField"}},{"kind":"Field","name":{"kind":"Name","value":"canDisplayPointField"}},{"kind":"Field","name":{"kind":"Name","value":"isImportScheduled"}},{"kind":"Field","name":{"kind":"Name","value":"importProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hasForecast"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"succeeded"}},{"kind":"Field","name":{"kind":"Name","value":"estimatedFinishTime"}},{"kind":"Field","name":{"kind":"Name","value":"actualFinishTime"}},{"kind":"Field","name":{"kind":"Name","value":"inQueue"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfJobsAheadInQueue"}},{"kind":"Field","name":{"kind":"Name","value":"sendEmail"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateScheduled"}},{"kind":"Field","name":{"kind":"Name","value":"updateProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hasForecast"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"succeeded"}},{"kind":"Field","name":{"kind":"Name","value":"estimatedFinishTime"}},{"kind":"Field","name":{"kind":"Name","value":"actualFinishTime"}},{"kind":"Field","name":{"kind":"Name","value":"inQueue"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfJobsAheadInQueue"}},{"kind":"Field","name":{"kind":"Name","value":"sendEmail"}}]}},{"kind":"Field","name":{"kind":"Name","value":"importedDataCount"}},{"kind":"Field","name":{"kind":"Name","value":"importedDataGeocodingRate"}},{"kind":"Field","alias":{"kind":"Name","value":"regionCount"},"name":{"kind":"Name","value":"importedDataCountOfAreas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"analyticalAreaType"},"value":{"kind":"EnumValue","value":"european_electoral_region"}}]},{"kind":"Field","alias":{"kind":"Name","value":"constituencyCount"},"name":{"kind":"Name","value":"importedDataCountOfAreas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"analyticalAreaType"},"value":{"kind":"EnumValue","value":"parliamentary_constituency"}}]},{"kind":"Field","alias":{"kind":"Name","value":"ladCount"},"name":{"kind":"Name","value":"importedDataCountOfAreas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"analyticalAreaType"},"value":{"kind":"EnumValue","value":"admin_district"}}]},{"kind":"Field","alias":{"kind":"Name","value":"wardCount"},"name":{"kind":"Name","value":"importedDataCountOfAreas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"analyticalAreaType"},"value":{"kind":"EnumValue","value":"admin_ward"}}]},{"kind":"Field","name":{"kind":"Name","value":"fieldDefinitions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"editable"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updateMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"destinationColumn"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sharingPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteUpdateConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteUpdateConfig"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteExternalDataSource"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; diff --git a/nextjs/src/app/(logged-in)/data-sources/create/configure/[externalDataSourceId]/page.tsx b/nextjs/src/app/(logged-in)/data-sources/create/configure/[externalDataSourceId]/page.tsx deleted file mode 100644 index 1a454e0a5..000000000 --- a/nextjs/src/app/(logged-in)/data-sources/create/configure/[externalDataSourceId]/page.tsx +++ /dev/null @@ -1,197 +0,0 @@ -'use client' - -import { FetchResult, gql, useMutation, useQuery } from '@apollo/client' -import { useRouter } from 'next/navigation' -import { useContext, useEffect } from 'react' -import { toast } from 'sonner' - -import { - ExternalDataSourceInput, - GetSourceMappingQuery, - GetSourceMappingQueryVariables, - UpdateExternalDataSourceMutation, - UpdateExternalDataSourceMutationVariables, -} from '@/__generated__/graphql' -import { UpdateMappingForm } from '@/components/UpdateMappingForm' -import { Button } from '@/components/ui/button' -import { LoadingIcon } from '@/components/ui/loadingIcon' -import { UPDATE_EXTERNAL_DATA_SOURCE } from '@/lib/graphql/mutations' -import { triggerAnalyticsEvent } from '@/lib/posthogutils' -import { formatCrmNames } from '@/lib/utils' - -import { CreateAutoUpdateFormContext } from '../../NewExternalDataSourceWrapper' - -const GET_UPDATE_CONFIG = gql` - query GetSourceMapping($ID: ID!) { - externalDataSource(pk: $ID) { - id - autoImportEnabled - autoUpdateEnabled - allowUpdates - hasWebhooks - updateMapping { - destinationColumn - source - sourcePath - } - fieldDefinitions { - label - value - description - editable - } - crmType - geographyColumn - geographyColumnType - geocodingConfig - usesValidGeocodingConfig - postcodeField - firstNameField - lastNameField - emailField - phoneField - addressField - canDisplayPointField - } - } -` - -export default function Page({ - params: { externalDataSourceId }, -}: { - params: { externalDataSourceId: string } -}) { - const router = useRouter() - const context = useContext(CreateAutoUpdateFormContext) - - useEffect(() => { - context.setStep(3) - }, [context]) - - const [updateSource, configResult] = useMutation< - UpdateExternalDataSourceMutation, - UpdateExternalDataSourceMutationVariables - >(UPDATE_EXTERNAL_DATA_SOURCE) - - const externalDataSource = useQuery< - GetSourceMappingQuery, - GetSourceMappingQueryVariables - >(GET_UPDATE_CONFIG, { - variables: { - ID: externalDataSourceId, - }, - }) - - function submit( - input: ExternalDataSourceInput, - e?: React.BaseSyntheticEvent | undefined - ) { - e?.preventDefault() - const create = updateSource({ - variables: { input: { id: externalDataSourceId, ...input } }, - }) - toast.promise(create, { - loading: 'Saving...', - success: (d: FetchResult) => { - if (!d.errors && d.data) { - router.push( - `/data-sources/create/review/${d.data.updateExternalDataSource.id}` - ) - } - triggerAnalyticsEvent('Data source created successfully', { - datasource: d.data?.updateExternalDataSource.__typename, - remoteName: d.data?.updateExternalDataSource.name, - }) - return 'Saved' - }, - error: (e: any) => { - triggerAnalyticsEvent('Data source creation failed', { - message: e.message, - }) - return `Couldn't save` - }, - }) - } - - if (externalDataSource.loading) { - return - } - - if (!externalDataSource.data?.externalDataSource.allowUpdates) { - return ( - - ) - } - - return ( -
-
-

- Now configure how you{"'"}d like data to be updated -

-

- Choose from the following data sources to enhance your{' '} - {formatCrmNames( - externalDataSource.data?.externalDataSource.crmType || 'database' - )}{' '} - with data that empowers your organisation. For geographic data, we - need to know which field has the postcode so we can make sure you are - getting accurate data. -

-
- {externalDataSource.data ? ( - ({ - source: m.source, - sourcePath: m.sourcePath, - destinationColumn: m.destinationColumn, - }) - ), - }} - fieldDefinitions={ - externalDataSource.data?.externalDataSource.fieldDefinitions - } - onSubmit={submit} - saveButtonLabel="Continue" - > - - - - ) : null} -
- ) -} diff --git a/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/oauthQueries.ts b/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/oauthQueries.ts deleted file mode 100644 index f0630d5d5..000000000 --- a/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/oauthQueries.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client' - -export const GOOGLE_SHEETS_OAUTH_URL = gql` - query GoogleSheetsOauthUrl($redirectUrl: String!) { - googleSheetsOauthUrl(redirectUrl: $redirectUrl) - } -` - -export const GOOGLE_SHEETS_OAUTH_CREDENTIALS = gql` - query GoogleSheetsOauthCredentials( - $redirectSuccessUrl: String! - $externalDataSourceId: String - ) { - googleSheetsOauthCredentials( - redirectSuccessUrl: $redirectSuccessUrl - externalDataSourceId: $externalDataSourceId - ) - } -` diff --git a/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/page.tsx b/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/page.tsx deleted file mode 100644 index 2ac3bb3ae..000000000 --- a/nextjs/src/app/(logged-in)/data-sources/create/connect/[externalDataSourceType]/page.tsx +++ /dev/null @@ -1,1432 +0,0 @@ -'use client' - -import { FetchResult, gql, useLazyQuery, useMutation } from '@apollo/client' -import { useAtomValue } from 'jotai' -import { camelCase } from 'lodash' -import { useRouter, useSearchParams } from 'next/navigation' -import { useContext, useEffect, useMemo, useState } from 'react' -import { FieldPath, FormProvider, useForm } from 'react-hook-form' - -import { - CreateExternalDataSourceInput, - CreateSourceMutation, - CrmType, - DataSourceType, - ExternalDataSourceInput, - GeographyTypes, - GoogleSheetsOauthCredentialsQuery, - GoogleSheetsOauthCredentialsQueryVariables, - GoogleSheetsOauthUrlQuery, - GoogleSheetsOauthUrlQueryVariables, - TestDataSourceQuery, - TestDataSourceQueryVariables, -} from '@/__generated__/graphql' -import { PreopulatedSelectField } from '@/components/ExternalDataSourceFields' -import { getFieldsForDataSourceType } from '@/components/UpdateExternalDataSourceFields' -import { Button } from '@/components/ui/button' -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form' -import { Input } from '@/components/ui/input' -import { LoadingIcon } from '@/components/ui/loadingIcon' -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { locationTypeOptions } from '@/lib/location' -import { currentOrganisationIdAtom } from '@/lib/organisation' -import { toastPromise } from '@/lib/toast' - -import { dataTypeIcons } from '@/lib/data' -import { formatCrmNames } from '@/lib/utils' -import { CreateAutoUpdateFormContext } from '../../NewExternalDataSourceWrapper' -import { - GOOGLE_SHEETS_OAUTH_CREDENTIALS, - GOOGLE_SHEETS_OAUTH_URL, -} from './oauthQueries' - -const TEST_DATA_SOURCE = gql` - query TestDataSource($input: CreateExternalDataSourceInput!) { - testDataSource(input: $input) { - __typename - crmType - fieldDefinitions { - label - value - description - editable - } - geographyColumn - geographyColumnType - geocodingConfig - usesValidGeocodingConfig - healthcheck - predefinedColumnNames - defaultDataType - remoteName - allowUpdates - defaults - oauthCredentials - } - } -` - -const CREATE_DATA_SOURCE = gql` - mutation CreateSource($input: CreateExternalDataSourceInput!) { - createExternalDataSource(input: $input) { - code - errors { - message - } - result { - id - name - crmType - dataType - allowUpdates - } - } - } -` - -type FormInputs = CreateExternalDataSourceInput & - ExternalDataSourceInput & { - temp?: { - airtableBaseUrl?: string - actionnetworkGroupUrl?: string - } - } - -export default function Page({ - params: { externalDataSourceType }, -}: { - params: { externalDataSourceType: keyof CreateExternalDataSourceInput } -}) { - const orgId = useAtomValue(currentOrganisationIdAtom) - const router = useRouter() - const context = useContext(CreateAutoUpdateFormContext) - - useEffect(() => { - context.setStep(2) - }, [context]) - - const defaultValues: CreateExternalDataSourceInput & ExternalDataSourceInput = - { - name: '', - geographyColumnType: GeographyTypes.Postcode, - geographyColumn: '', - dataType: context.dataType, - airtable: { - apiKey: '', - baseId: '', - tableId: '', - }, - mailchimp: { - apiKey: '', - listId: '', - }, - actionnetwork: { - apiKey: '', - groupSlug: '', - }, - editablegooglesheets: { - oauthCredentials: '', - spreadsheetId: '', - sheetName: '', - }, - tickettailor: { - apiKey: '', - }, - } - - const form = useForm({ - defaultValues: { - ...defaultValues, - } as FormInputs, - }) - - const searchParams = useSearchParams() - - const [googleSheetsOauthCredentials, googleSheetsOauthCredentialsResult] = - useLazyQuery< - GoogleSheetsOauthCredentialsQuery, - GoogleSheetsOauthCredentialsQueryVariables - >(GOOGLE_SHEETS_OAUTH_CREDENTIALS) - - useEffect(() => { - // The presence of these URL parameters indicates an OAuth redirect - // back from Google. - if (searchParams.get('state') && searchParams.get('code')) { - // The presence of this session key indicates an OAuth journey - // that started from an existing data source. Google does not accept - // dynamic redirect URLs, so this page has to handle both cases. - if (sessionStorage.existingDataSourceOAuthRedirect) { - const url = new URL(sessionStorage.existingDataSourceOAuthRedirect) - url.searchParams.set('state', searchParams.get('state')!) - url.searchParams.set('code', searchParams.get('code')!) - url.searchParams.set('scope', searchParams.get('scope')!) - window.location.href = url.toString() - return - } - // Convert the URL parameters into oauth_credentials using - // the GoogleSheetsOauthCredentialsQuery, then save the - // credentials in the form object. - toastPromise( - googleSheetsOauthCredentials({ - variables: { redirectSuccessUrl: window.location.href }, - }), - { - loading: 'Completing Google authorization...', - success: (d: FetchResult) => { - if (!d.errors && d.data?.googleSheetsOauthCredentials) { - form.setValue( - 'editablegooglesheets.oauthCredentials', - d.data.googleSheetsOauthCredentials - ) - return 'Google authorization succeeded' - } - throw new Error('Google authorization failed') - }, - error: () => { - return 'Google authorization failed' - }, - } - ) - } - }, []) // No dependencies here so useEffect only runs once - - const dataType = form.watch('dataType') as DataSourceType - const collectFields = useMemo(() => { - return getFieldsForDataSourceType(dataType) - }, [dataType]) - - const geographyFields = [ - 'geographyColumn', - 'geographyColumnType', - 'geocodingConfig', - ] - - async function fetchSheetNamesUsingCredentials( - spreadsheetId: string, - oauthCredentials: string - ): Promise { - const parsedCredentials = JSON.parse(oauthCredentials) - const accessToken = parsedCredentials.access_token - - const headers = { - Authorization: `Bearer ${accessToken}`, - } - const response = await fetch( - `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`, - { - method: 'GET', - headers, - } - ) - - if (!response.ok) { - const error = await response.json() - console.error('API Error:', error) - throw new Error(error.error.message || 'Failed to fetch sheet names') - } - const data = await response.json() - const sheets = data.sheets || [] - return sheets.map( - (sheet: { properties: { title: string } }) => sheet.properties.title - ) - } - const [sheetNames, setSheetNames] = useState([]) - const [loadingSheets, setLoadingSheets] = useState(false) - const [fetchError, setFetchError] = useState(null) - const [sheetUrl, setSheetUrl] = useState('') - - function extractBaseAndTableId(url: string): { - baseId?: string - tableId?: string - } { - try { - const match = url.match(/\/(app[a-zA-Z0-9]+)\/(tbl[a-zA-Z0-9]+)/) - if (match) { - const [, baseId, tableId] = match - return { baseId, tableId } - } - return {} - } catch (error) { - console.error('Error extracting Base ID and Table ID:', error) - return {} - } - } - - function extractActionNetworkGroupSlug(url: string): string | null { - try { - const match = url.match(/\/groups\/([a-zA-Z0-9-]+)\//) - return match ? match[1] : null - } catch (error) { - console.error('Error extracting group slug:', error) - return null - } - } - - async function handleSheetUrlChange( - url: string, - setSheetUrl: React.Dispatch>, - form: ReturnType, - setLoadingSheets: React.Dispatch>, - setSheetNames: React.Dispatch>, - fetchSheetNamesUsingCredentials: ( - spreadsheetId: string, - oauthCredentials: string - ) => Promise, - setFetchError: React.Dispatch> - ) { - setSheetUrl(url) - try { - const match = url.match(/\/d\/([a-zA-Z0-9-_]+)/) - if (match && match[1]) { - const spreadsheetId = match[1] - form.setValue('editablegooglesheets.spreadsheetId', spreadsheetId) - setLoadingSheets(true) - const oauthCredentials = form.getValues( - 'editablegooglesheets.oauthCredentials' - ) - if (oauthCredentials) { - const sheets = await fetchSheetNamesUsingCredentials( - spreadsheetId, - oauthCredentials - ) - setSheetNames(sheets) - } else { - throw new Error('OAuth credentials not available') - } - - setLoadingSheets(false) - } - } catch (err) { - setFetchError( - 'Failed to fetch sheet names. Please check the URL or credentials.' - ) - setLoadingSheets(false) - } - } - - const [createSource, createSourceResult] = - useMutation(CREATE_DATA_SOURCE) - const [testSource, testSourceResult] = useLazyQuery< - TestDataSourceQuery, - TestDataSourceQueryVariables - >(TEST_DATA_SOURCE) - const [googleSheetsOauthUrl, googleSheetsOauthUrlResult] = useLazyQuery< - GoogleSheetsOauthUrlQuery, - GoogleSheetsOauthUrlQueryVariables - >(GOOGLE_SHEETS_OAUTH_URL) - const [googleSheetsError, setGoogleSheetsError] = useState('') - - const currentSource = testSourceResult.data - - const [guessed, setGuessed] = useState< - Partial, string | undefined | null>> - >({}) - - /** - * For a field that maps a data source property (e.g. address_field) to - * a field on the remote data source (e.g. "Address Line 1"), try to guess the - * remote field based on a list of likely options, while preventing bad matches - * (e.g. "Email address" for "Address"). - * - * In this example, field = "addressField", guessKeys = ["address", "line1", ...], - * badKeys = ["email"]. - */ - function useGuessedField( - field: keyof FormInputs, - guessKeys: string[], - badKeys: string[] = [] - ) { - useEffect(() => { - // If this data isn't being collected for this source type, set the source - // field for this data to null. This prevents accidentally trying to collect - // invalid data (for example start times for data that is not events). - // @ts-ignore - if (!collectFields.includes(field) && !geographyFields.includes(field)) { - form.setValue(field, null) - return - } - const guess = - testSourceResult.data?.testDataSource.fieldDefinitions?.find( - (field: { label?: string | null; value: string }) => { - const isMatch = ( - fieldName: string | null | undefined, - guessKey: string - ) => { - if (!fieldName) { - return false - } - const match = fieldName - .toLowerCase() - .replaceAll(' ', '') - .includes(guessKey.replaceAll(' ', '')) - if (!match) { - return false - } - for (const badKey of badKeys) { - const badMatch = fieldName - .toLowerCase() - .replaceAll(' ', '') - .includes(badKey.replaceAll(' ', '')) - if (badMatch) { - return false - } - } - return true - } - for (const guessKey of guessKeys) { - if ( - isMatch(field.label, guessKey) || - isMatch(field.value, guessKey) - ) { - return true - } - } - } - ) - if (guess?.value) { - setGuessed((guesses) => ({ ...guesses, [field]: guess?.value })) - // @ts-ignore - form.setValue(field, guess?.value) - } - }, [ - testSourceResult.data?.testDataSource.fieldDefinitions, - form, - collectFields, - setGuessed, - ]) - } - - useGuessedField('geographyColumn', [ - 'postcode', - 'postal code', - 'zip code', - 'zip', - ]) - useGuessedField('emailField', ['email']) - useGuessedField('phoneField', ['mobile', 'phone']) - useGuessedField( - 'addressField', - ['street', 'line1', 'address', 'location', 'venue'], - ['email'] - ) - useGuessedField('fullNameField', ['full name', 'name']) - useGuessedField('firstNameField', ['first name', 'given name']) - useGuessedField('titleField', ['title', 'name']) - useGuessedField('descriptionField', [ - 'description', - 'body', - 'comments', - 'notes', - 'about', - ]) - useGuessedField('imageField', [ - 'image', - 'photo', - 'picture', - 'avatar', - 'attachment', - 'attachments', - 'file', - 'files', - 'graphic', - 'poster', - 'logo', - 'icon', - ]) - useGuessedField('startTimeField', [ - 'start', - 'start time', - 'start date', - 'begin', - 'beginning', - 'start_at', - 'start_time', - 'start_date', - 'date', - 'time', - 'datetime', - 'timestamp', - 'from', - ]) - useGuessedField('endTimeField', [ - 'end', - 'end time', - 'end date', - 'finish', - 'finish time', - 'finish date', - 'end_at', - 'end_time', - 'end_date', - 'until', - ]) - useGuessedField('publicUrlField', [ - 'public url', - 'public link', - 'public', - 'url', - 'link', - 'website', - 'webpage', - 'web', - 'page', - 'site', - 'href', - 'uri', - 'path', - 'slug', - 'permalink', - ]) - useGuessedField('socialUrlField', [ - 'social url', - 'social link', - 'social', - 'facebook', - 'instagram', - ]) - - useEffect(() => { - if (testSourceResult.data?.testDataSource?.defaultDataType) { - const dataType = testSourceResult.data.testDataSource - .defaultDataType as DataSourceType - context.dataType = dataType - form.setValue('dataType', dataType) - // Default dict - const defaultFieldValues = - testSourceResult.data.testDataSource.defaults || {} - for (const key in defaultFieldValues) { - const camelCasedKey = camelCase(key) as keyof FormInputs - const value = defaultFieldValues[key] - if (value !== null && value !== undefined) { - form.setValue(camelCasedKey, value) - } - } - } - }, [testSourceResult.data]) - - const airtableUrl = form.watch('temp.airtableBaseUrl') - - useEffect(() => { - if (airtableUrl) { - try { - const { baseId, tableId } = extractBaseAndTableId(airtableUrl) - if (baseId) { - form.setValue('airtable.baseId', baseId) - } - if (tableId) { - form.setValue('airtable.tableId', tableId) - } - } catch (e) { - // Invalid URL - form.setError('temp.airtableBaseUrl', { - type: 'validate', - message: 'Invalid URL', - }) - } - } - }, [airtableUrl]) - - async function submitTestConnection(formData: FormInputs) { - if (!formData[externalDataSourceType]) { - throw Error('Need some CRM connection details to proceed!') - } - - // To avoid mutation of the form data - const genericCRMData = Object.assign({}, formData) - const CRMSpecificData = formData[externalDataSourceType] - - // Remove specific CRM data from the generic data - // TODO: make this less fragile. Currently it assumes any nested - // object is specific to a CRM. - for (const key of Object.keys(formData)) { - if (typeof formData[key as keyof FormInputs] === 'object') { - delete genericCRMData[key as keyof FormInputs] - } - } - - const input: TestDataSourceQueryVariables['input'] = { - [externalDataSourceType]: { - ...genericCRMData, - ...CRMSpecificData, - }, - } - - toastPromise( - testSource({ - variables: { input }, - }), - { - loading: 'Testing connection...', - success: (d: FetchResult) => { - if ( - !d.errors && - d.data?.testDataSource && - d.data.testDataSource.healthcheck - ) { - return 'Connection is healthy' - } - throw new Error( - d.errors?.map((e) => e.message).join(', ') || 'Unknown error' - ) - }, - error: 'Connection failed', - } - ) - } - - async function submitCreateSource(formData: FormInputs) { - if (!formData[externalDataSourceType]) { - throw Error('Need some CRM connection details to proceed!') - } - // To avoid mutation of the form data - const genericCRMData = Object.assign({}, formData) - let CRMSpecificData = formData[externalDataSourceType] - - // Remove specific CRM data from the generic data - // TODO: make this less fragile. Currently it assumes any nested - // object is specific to a CRM. - for (const key of Object.keys(formData)) { - if (typeof formData[key as keyof FormInputs] === 'object') { - delete genericCRMData[key as keyof FormInputs] - } - } - - let input: CreateExternalDataSourceInput = { - [externalDataSourceType]: { - ...genericCRMData, - ...CRMSpecificData, - organisation: { set: orgId }, - }, - } - toastPromise(createSource({ variables: { input } }), { - loading: 'Saving connection...', - success: (d) => { - const errors = d.errors || d.data?.createExternalDataSource.errors || [] - if (!errors.length && d.data?.createExternalDataSource.result) { - if ( - d.data?.createExternalDataSource.result.dataType === - DataSourceType.Member && - d.data.createExternalDataSource.result.allowUpdates - ) { - router.push( - `/data-sources/create/configure/${d.data.createExternalDataSource.result.id}` - ) - } else { - router.push( - `/data-sources/inspect/${d.data.createExternalDataSource.result.id}` - ) - } - return 'Connection successful' - } - throw new Error( - errors.map((e) => e.message).join(', ') || 'Unknown error' - ) - }, - error(e) { - return { - title: 'Connection failed', - description: e.message, - } - }, - }) - } - - if ( - createSourceResult.loading || - createSourceResult.data?.createExternalDataSource.result - ) { - return ( -
-

Saving connection...

-

- Please wait whilst we save your connection details -

- -
- ) - } - - function FPreopulatedSelectField({ - name, - label, - placeholder, - required = false, - helpText = '', - }: { - name: FieldPath - label?: string - placeholder?: string - required?: boolean - helpText?: string - }) { - return ( - - ) - } - - if (currentSource?.testDataSource?.healthcheck) { - const dataTypes = Object.entries(dataTypeIcons).map(([key, value]) => ({ - value: key, - label: value.label, - icon: value.icon, - })) - - return ( -
-

Connection successful

-

- Tell us a bit more about the data you{"'"}re connecting to. -

- -
- - ( - - Nickname - - {/* @ts-ignore */} - - - - This will be visible to your team. - - - - )} - /> - {!currentSource?.testDataSource?.defaultDataType && ( - ( - - Data type - - - - - - )} - /> - )} - - {!currentSource?.testDataSource?.predefinedColumnNames && ( -
- ( - - Type of location data - - - - - - )} - /> - - {testSourceResult.data?.testDataSource.crmType === - CrmType.Editablegooglesheets && ( - - )} - {collectFields.map((field) => ( - - ))} -
- )} - - - -
-
- ) - } - - if (testSourceResult.loading) { - return ( -
-

Testing connection...

-

- Please wait whilst we try to connect to your{' '} - {formatCrmNames(externalDataSourceType || 'CRM')} using the - information you provided -

- -
- ) - } - - if (externalDataSourceType === 'airtable') { - return ( -
-
-

Connecting to your Airtable base

-

- In order to send data across to your Airtable, we{"'"}ll need a few - details that give us permission to make updates to your base, as - well as tell us which table to update in the first place. -

-
-
- -
Connection details
- ( - - Airtable URL - - { - const { baseId, tableId } = extractBaseAndTableId( - e.target.value - ) - if (baseId) form.setValue('airtable.baseId', baseId) - if (tableId) form.setValue('airtable.tableId', tableId) - }} - required - /> - - - The URL for your Airtable base. - - - - )} - /> - ( - - Airtable access token - - - -
- - Your token should have access to the base and the - following scopes: - -
    -
  • - data.records:read -
  • -
  • - data.records:write -
  • -
  • - schema.bases:read -
  • -
  • - webhook:manage -
  • -
- - Learn how to find your personal access token. - -
- -
- )} - /> - -
- - -
- - -
- ) - } - if (externalDataSourceType === 'mailchimp') { - return ( -
-
-

Connecting to your Mailchimp audience

-

- In order to send data across to your Mailchimp audience, we{"'"}ll - need a few details that gives us permission to make updates to your - audience, as well as tell us which audience to update in the first - place. -

-
-
- -
Connection details
- ( - - MailChimp API key - - {/* @ts-ignore */} - - - - {' '} - - Learn how to find your API key. - - - - - )} - /> - - ( - - Audience ID - - {/* @ts-ignore */} - - - - The unique identifier for your audience.{' '} - - Learn how to find your audience ID. - - - - - )} - /> - -
- - -
- - -
- ) - } - if (externalDataSourceType === 'actionnetwork') { - const groupSlug = form.watch('actionnetwork.groupSlug') - const actionNetworkApiUrl = groupSlug - ? `https://actionnetwork.org/groups/${groupSlug}/apis` - : '' - const showApiKeyField = !!groupSlug - - return ( -
-
-

- Connecting to your Action Network instance -

-

- In order to send data across to your Action Network instance, we - {"'"}ll need a few details that give us permission to make updates - to your members. -

-
-
- -
Connection details
- ( - - Action Network Group URL - - { - const slug = extractActionNetworkGroupSlug( - e.target.value - ) - if (slug) { - form.setValue('actionnetwork.groupSlug', slug) - } else { - form.setError('temp.actionnetworkGroupUrl', { - type: 'validate', - message: 'Invalid URL', - }) - } - }} - required - /> - - - Paste the URL of your Action Network group here. - - - - )} - /> - {groupSlug && ( -

- Use the following link to access your API key settings: - - {actionNetworkApiUrl} - -

- )} - {showApiKeyField && ( - ( - - Action Network API key - - - - - - )} - /> - )} -
- - -
- - -
- ) - } - if (externalDataSourceType === 'editablegooglesheets') { - const hasOauthParams = searchParams.get('state') && searchParams.get('code') - // The presence of the params and absence of an oauthCredentialsResult - // means the query has either not yet been sent, or is in progress. - // Checking this instead of the `loading` property catches the - // case where the page has been loaded but the query hasn't - // yet been sent. - const oauthCredentialsLoading = - hasOauthParams && - !googleSheetsOauthCredentialsResult.data && - !googleSheetsOauthCredentialsResult.error - if (oauthCredentialsLoading) { - return ( -
-
-

- Connecting to your Google Sheets spreadsheet -

-
- -
- ) - } - if (!form.watch('editablegooglesheets.oauthCredentials')) { - return ( -
-
-

- Connecting to your Google Sheets spreadsheet -

-

- Click the button below to grant Mapped permission to access your - spreadsheet. -

-
-
- - -
- {googleSheetsError && ( - {googleSheetsError} - )} -
- ) - } - return ( -
-
-

- Connecting to your Google Sheets spreadsheet -

-

- Now we just need a few details to know which spreadsheet to import - and update. -

-
-
- - ( - - Google Sheets URL - - - handleSheetUrlChange( - e.target.value, - setSheetUrl, - form, - setLoadingSheets, - setSheetNames, - fetchSheetNamesUsingCredentials, - setFetchError - ) - } - required - /> - - - Paste the URL of your Google Sheets document - - - - )} - /> - ( - - Sheet Name - - - - {fetchError && ( - {fetchError} - )} - - - )} - /> -
- - -
- - -
- ) - } - - if (externalDataSourceType === 'tickettailor') { - return ( -
-
-

- Connecting to your Ticket Tailor box office -

-

- In order to import data from your Ticket Tailor box office, we{"'"} - ll need a few details. -

-
-
- -
Connection details
- ( - - Ticket Tailor API key - - {/* @ts-ignore */} - - - - Your API key can be found or generated in the Box Office - Settings under API.{' '} - - Guide to finding your API key. - - - - - )} - /> - -
- - -
- - -
- ) - } - - return null -} diff --git a/nextjs/src/app/(logged-in)/data-sources/create/page.tsx b/nextjs/src/app/(logged-in)/data-sources/create/page.tsx deleted file mode 100644 index 31eb018ae..000000000 --- a/nextjs/src/app/(logged-in)/data-sources/create/page.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client' - -import { useRouter } from 'next/navigation' -import { useContext, useEffect, useState } from 'react' -import { twMerge } from 'tailwind-merge' - -import { Button } from '@/components/ui/button' -import { externalDataSourceOptions } from '@/lib/data' - -import { CreateAutoUpdateFormContext } from './NewExternalDataSourceWrapper' - -export default function Page() { - const router = useRouter() - const context = useContext(CreateAutoUpdateFormContext) - const [source, setSource] = useState(null) - - useEffect(() => { - context.setStep(1) - }, [context]) - - return ( -
-
-

Select platform to sync data to

-

- We currently support the following platforms. If your platform isn’t - on this list,{' '} - - get in touch to see how we can help. - -

-
- {Object.values(externalDataSourceOptions) - .filter((externalDataSource) => externalDataSource.supported) - .map((externalDataSource) => ( -
-
{ - setSource(externalDataSource.key) - }} - className={twMerge( - 'cursor-pointer rounded-3xl bg-meepGray-700 px-10 py-6 overflow-hidden flex flex-row items-center justify-center transition-all hover:border-brandBlue border-2 box-border', - source === externalDataSource.key && 'border-brandBlue border-2' - )} - > - -
-
- ))} - -
- ) -} diff --git a/nextjs/src/app/(logged-in)/data-sources/create/review/[externalDataProviderId]/page.tsx b/nextjs/src/app/(logged-in)/data-sources/create/review/[externalDataProviderId]/page.tsx deleted file mode 100644 index 8d114bbe9..000000000 --- a/nextjs/src/app/(logged-in)/data-sources/create/review/[externalDataProviderId]/page.tsx +++ /dev/null @@ -1,167 +0,0 @@ -'use client' - -import { gql, useQuery } from '@apollo/client' -import { useRouter } from 'next/navigation' -import { useContext, useEffect } from 'react' - -import { - AutoUpdateCreationReviewQuery, - AutoUpdateCreationReviewQueryVariables, -} from '@/__generated__/graphql' -import { DataSourceFieldLabel } from '@/components/DataSourceIcon' -import { - DATA_SOURCE_FRAGMENT, - ExternalDataSourceCard, - TriggerUpdateButton, -} from '@/components/ExternalDataSourceCard' -import { Button } from '@/components/ui/button' -import { LoadingIcon } from '@/components/ui/loadingIcon' -import { formatCrmNames } from '@/lib/utils' - -import { CreateAutoUpdateFormContext } from '../../NewExternalDataSourceWrapper' - -const GET_UPDATE_CONFIG = gql` - query AutoUpdateCreationReview($ID: ID!) { - externalDataSource(pk: $ID) { - id - name - geographyColumn - geographyColumnType - geocodingConfig - usesValidGeocodingConfig - dataType - crmType - autoImportEnabled - autoUpdateEnabled - updateMapping { - source - sourcePath - destinationColumn - } - jobs(pagination: { limit: 10 }) { - lastEventAt - status - } - automatedWebhooks - webhookUrl - ...DataSourceCard - } - } - ${DATA_SOURCE_FRAGMENT} -` - -export default function Page({ - params: { externalDataProviderId }, -}: { - params: { externalDataProviderId: string } -}) { - const router = useRouter() - const context = useContext(CreateAutoUpdateFormContext) - - useEffect(() => { - context.setStep(4) - }, [context]) - - const pageQuery = useQuery< - AutoUpdateCreationReviewQuery, - AutoUpdateCreationReviewQueryVariables - >(GET_UPDATE_CONFIG, { - variables: { - ID: externalDataProviderId, - }, - }) - - if (pageQuery.error) { - return ( -
-

Error

-

{pageQuery.error.message}

-
- ) - } - - return ( -
- {pageQuery.data?.externalDataSource.geographyColumn ? ( -
-

Activate data source

-

- You{"'"}re almost there! -

-
    - {pageQuery.data?.externalDataSource.automatedWebhooks ? ( -
  • - - Active auto-update webhooks to start updating your data source - when the - - - field changes. -
  • - ) : pageQuery.data?.externalDataSource.webhookUrl ? ( -
  • -
    -

    Webhook URL for auto-imports and auto-updates:

    - - {pageQuery.data.externalDataSource.webhookUrl} - - -

    - Turn the below switches on once you have added the above - Webhook URL to your{' '} - {formatCrmNames( - pageQuery.data?.externalDataSource.crmType || 'database' - )}{' '} - and enabled it. -

    -
    -
  • - ) : null} -
  • - Trigger an update to see the data source in action. -
  • -
-
- ) : ( -
-

You{"'"}re done!

-

- You can now use this data source. -

-
- )} - {pageQuery.loading || !pageQuery.data ? ( - - ) : ( - <> -
- -
-
- -
- - )} - -
- ) -} diff --git a/nextjs/src/app/(logged-in)/data-sources/create/NewExternalDataSourceWrapper.tsx b/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/NewExternalDataSourceWrapper.tsx similarity index 100% rename from nextjs/src/app/(logged-in)/data-sources/create/NewExternalDataSourceWrapper.tsx rename to nextjs/src/app/(logged-in-fullscreen)/data-sources/create/NewExternalDataSourceWrapper.tsx diff --git a/nextjs/src/app/(logged-in)/data-sources/create/layout.tsx b/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/layout.tsx similarity index 62% rename from nextjs/src/app/(logged-in)/data-sources/create/layout.tsx rename to nextjs/src/app/(logged-in-fullscreen)/data-sources/create/layout.tsx index dba19f69e..b5e6eb5dd 100644 --- a/nextjs/src/app/(logged-in)/data-sources/create/layout.tsx +++ b/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/layout.tsx @@ -1,8 +1,5 @@ -import { Metadata } from 'next' - import { requireAuth } from '@/lib/server-auth' - -import NewExternalDataSourceWrapper from './NewExternalDataSourceWrapper' +import { Metadata } from 'next' export default async function Layout({ children, @@ -10,8 +7,7 @@ export default async function Layout({ children: React.ReactNode }) { await requireAuth() - - return {children} + return children } export const metadata: Metadata = { diff --git a/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/page.tsx b/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/page.tsx new file mode 100644 index 000000000..ef27c4ca6 --- /dev/null +++ b/nextjs/src/app/(logged-in-fullscreen)/data-sources/create/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { CreateDataSourceFlow } from '@/components/data-sources/configure/CreateDataSourceFlow' + +export default function Page() { + return +} diff --git a/nextjs/src/app/(logged-in)/graphiql/layout.tsx b/nextjs/src/app/(logged-in-fullscreen)/graphiql/layout.tsx similarity index 100% rename from nextjs/src/app/(logged-in)/graphiql/layout.tsx rename to nextjs/src/app/(logged-in-fullscreen)/graphiql/layout.tsx diff --git a/nextjs/src/app/(logged-in)/graphiql/page.tsx b/nextjs/src/app/(logged-in-fullscreen)/graphiql/page.tsx similarity index 100% rename from nextjs/src/app/(logged-in)/graphiql/page.tsx rename to nextjs/src/app/(logged-in-fullscreen)/graphiql/page.tsx diff --git a/nextjs/src/app/(logged-in-fullscreen)/layout.tsx b/nextjs/src/app/(logged-in-fullscreen)/layout.tsx new file mode 100644 index 000000000..1dc47d3d5 --- /dev/null +++ b/nextjs/src/app/(logged-in-fullscreen)/layout.tsx @@ -0,0 +1,23 @@ +import { Toaster } from 'sonner' + +import Navbar from '@/components/navbar' +import { loadUser } from '@/lib/server-auth' + +export default async function Layout({ + children, +}: { + children: React.ReactNode +}) { + const user = await loadUser() + const isLoggedIn = Boolean(user) + + return ( +
+ +
+ {children} +
+ +
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/CreateDataSourceFlow.tsx b/nextjs/src/components/data-sources/configure/CreateDataSourceFlow.tsx new file mode 100644 index 000000000..cd47c9d85 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/CreateDataSourceFlow.tsx @@ -0,0 +1,43 @@ +// import { useAtom } from 'jotai' +// import { atomWithMachine } from 'jotai-xstate' +// import { createMachine } from 'xstate' + +import { Button } from '@/components/ui/button' +import { useAtom } from 'jotai' +import { twMerge } from 'tailwind-merge' +import { SOURCE_SETUP_STEPS } from './screens' +import { configureDataSourceStateAtom } from './state' + +export function CreateDataSourceFlow() { + const [state, set] = useAtom(configureDataSourceStateAtom) + const Screen = + SOURCE_SETUP_STEPS.find((s) => s.key === (state.value as any))?.screen || + (() => { + return
Unknown screen
+ }) + + return ( +
+ +
+ +
+
+ + +
+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/Geocoding.tsx b/nextjs/src/components/data-sources/configure/Geocoding.tsx new file mode 100644 index 000000000..c31868092 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/Geocoding.tsx @@ -0,0 +1,7 @@ +export default function GeocodingScreen() { + return ( +
+

Geocoding

+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/Ingest.tsx b/nextjs/src/components/data-sources/configure/Ingest.tsx new file mode 100644 index 000000000..0bb418d64 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/Ingest.tsx @@ -0,0 +1,40 @@ +// List + +import { CrmType } from '@/__generated__/graphql' +import { externalDataSourceOptions } from '@/lib/data' +import { useState } from 'react' +import { twMerge } from 'tailwind-merge' + +export default function IngestScreen() { + const [source, setSource] = useState(null) + + return ( +
+

Where does your data come from?

+

+ Select the source of your data. You can upload a file or connect a third + party tool to regularly sync data between. +

+
+ {Object.values(externalDataSourceOptions) + .filter((externalDataSource) => externalDataSource.supported) + .map((externalDataSource) => ( +
{ + setSource(externalDataSource.key) + }} + > + + {externalDataSource.name} +
+ ))} +
+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/Mapping.tsx b/nextjs/src/components/data-sources/configure/Mapping.tsx new file mode 100644 index 000000000..d30b2d2dd --- /dev/null +++ b/nextjs/src/components/data-sources/configure/Mapping.tsx @@ -0,0 +1,7 @@ +export default function MappingScreen() { + return ( +
+

Mapping

+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/Metadata.tsx b/nextjs/src/components/data-sources/configure/Metadata.tsx new file mode 100644 index 000000000..f19da83e3 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/Metadata.tsx @@ -0,0 +1,7 @@ +export default function MetadataScreen() { + return ( +
+

Metadata

+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/Review.tsx b/nextjs/src/components/data-sources/configure/Review.tsx new file mode 100644 index 000000000..cac794233 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/Review.tsx @@ -0,0 +1,7 @@ +export default function ReviewScreen() { + return ( +
+

Review

+
+ ) +} diff --git a/nextjs/src/components/data-sources/configure/screens.ts b/nextjs/src/components/data-sources/configure/screens.ts new file mode 100644 index 000000000..773da6f96 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/screens.ts @@ -0,0 +1,34 @@ +import GeocodingScreen from './Geocoding' +import IngestScreen from './Ingest' +import MappingScreen from './Mapping' +import MetadataScreen from './Metadata' +import ReviewScreen from './Review' +import { SetupStep } from './state' + +export const SOURCE_SETUP_STEPS = [ + { + key: SetupStep.Ingest, + label: 'Connect', + screen: IngestScreen, + }, + { + key: SetupStep.Metadata, + label: 'Basic info', + screen: MetadataScreen, + }, + { + key: SetupStep.Geocoding, + label: 'Location matching', + screen: GeocodingScreen, + }, + { + key: SetupStep.Mapping, + label: 'Data tagging', + screen: MappingScreen, + }, + { + key: SetupStep.Review, + label: 'Review', + screen: ReviewScreen, + }, +] as const diff --git a/nextjs/src/components/data-sources/configure/state.ts b/nextjs/src/components/data-sources/configure/state.ts new file mode 100644 index 000000000..1128870d0 --- /dev/null +++ b/nextjs/src/components/data-sources/configure/state.ts @@ -0,0 +1,77 @@ +import { atomWithMachine } from 'jotai-xstate' +import { createMachine } from 'xstate' + +export enum SetupStep { + Ingest = 'Ingest', + Metadata = 'Metadata', + Geocoding = 'Geocoding', + Mapping = 'Mapping', + Review = 'Review', +} + +export const createConfigureDataSourceStateMachine = () => + createMachine({ + /** @xstate-layout N4IgpgJg5mDOIC5QGMD2A7AZgSygVwCcwARAQwBdSBlVQ5MAOgEl0ZZyBidMAD3IG0ADAF1EoAA6pY2ctgxiQPRAEYAnMoYAWAGzblmgKwHNmgEwB2fdoA0IAJ6IAHAGZnWx6uenTB1avMGps4AvsG2aFi4hCQU1LQE9MyscJwsbAIiCpLSsvJIioiGDK6qjgaCpoJl2s7a5rYOCC4GDMaayqaqdVWCaqHhGDj4RGSUNHSMaSkcALJglBCxQqL52TJy6ApKCGoaOnqGbRZWDU4uxfqOmo6Czubm2mb9IBFD0aNxE0npHADiYKg0BBsKxllkpOs8qBtrstLp9EYTMcdKcds4NJdNM5HKZtAY9Pjnq8oiNYuMEpNkuxZqRxOIQVAwasIblNvkYeo4QdEWZLCj7Cobgx-M4undbtoLAYQmEXoMSTExvFElNqQAlMAAN2wYAA7kyJCyNlsVJz9gijnybAKmhovJ0OjiHhLtET5cNFZ8KQw5gtYhwAEakZAAawNIDWrJNCCCLTUgkEfi88fUmlRnjc+lu5mcmgC9xqbsiHo+5MSvtIi0oXF4GRWhpyxvZiAMykEwvujnMCeM+iCqPa7fMfmuqeUNVUeaLb1JSq+FarpA4qrr4MbUIKMdMmmFk5u5kcHnR2IMqIPbl0d384+U5k0gk004VpeVjAX-vflHDkab0MQzlbBgOmUNtfHKXETFPG0u1MBhBDqQ9LC7B8DEcJ8SzJV8fXmSt-X+QFUGBUFMmZdc2T-BBW3bfwD27QRezMZx028BgAgg29tGvED0PeTD5xwxcaTpBlvyNDdtiojtaJ7dpGNRTj23hSVu0qKoTB42cvXLAT-Q1bU9VEsjo0kmjkPo2T+xtZxemKRw6gg7wagAjTPTLRh8KBBlA2DMMSIbSFyM3MpHGKPwc1ce9VB8UxUTbLEgLxe8TEQ28XJfL4PMIrzuD4QyAujOyNF0YcAlcIxULPTkSi7VR6MEbtWzSvjvUyoioGXKlV1I-Lmyaa4GEqEx2lqlwmJtBEd28KLlBuFw70sJq5xagFPNYWYdK-PyIzEwLthxHc81FAI7OMCp6nGxNYL8S6-FxcxOkWrT3JWrK1takStp-cTEBHYprPu2rtBcS6z3RBggaCIJEvvXxHrchh3rWmZaXpYj622ozeuBgauweQH2jKc7GkeDQ7NcM7al0Tw4awxH2r0nV9U+nboxA1xhWqA8DHC9EzzMODVDAkDLGHKoafnFGvKDUM8qjLH9HB-Nh3xLwuyg4nOSBw9TEcW8dF10xxe9ZHhLWnKuv8uWKN1nc6m55Wah1gJUWMWDTA6HwZu0eDDEfWViQwpby0ltaV1l39NzaVp4OlII-Bm-lGmUVw3F1x4jFqPN2kN-33V4oO3xD9rP1IcPvsoypWOdS98R8Z2bUedsXD0XpUMqe6-YGYt86en0i7+F62rL3anAVu37kFx21b5jRWyxaU9FxTwc67mdXKwk3UeLovh+jXRFNVtQjHUR55NqOCH3o0VE27IGjeD036a1Rnd-l22lcn1X68aXxYOze4AYnTvPfRgDM9TeRlszTGFFhbmFYomR2AEEF2XTCBVigQSZ40sNxXO3dNLwzAbqDq6RX4wPHHAueuZEwzTuLmWK3NbbDVmmdaUICGCEPWn6Ta6Mvojx2PoNw54uzXACCYPQsV3ZuHvLUKK6ccyBDYRwumpDNxszcKUfEXMebKEqhoaaeZ96lAqKoRRz9wGbw+jwlmvUQKaFUK0fwuh9xmE6DFG0ZgNDDj3GOCcU5cFr3St6DhhCVEwgAhee8NQegAUFurFQ5Q4HKGMCrSUST1DmFCLKdAhE4AKADj3Nya4eoUQALSwX3vofwqZaiuDcY0EpLQ-BNOac04cbCVxFKtpuPMqIPDthkXiKih59BsJLp0iO2wAKwQPB0aUvR06OGYtoBgR5s6SnKNzEx-jnzNUSHTcZ5c-CNJGshACWczx2WjgeOxl12htO2YHXuFjWAHL4boN2UUaIYK-vJWqKzOibNzPcGaWzV47ILuwsxupXmswAi0ay9U7IoVbN-eJegOYJj6b4b2tRMnBCAA */ + id: 'configureDataSource', + context: {}, + initial: SetupStep.Ingest, + states: { + [SetupStep.Ingest]: { + on: { + next: SetupStep.Metadata, + [SetupStep.Ingest]: SetupStep.Ingest, + [SetupStep.Metadata]: SetupStep.Metadata, + [SetupStep.Geocoding]: SetupStep.Geocoding, + [SetupStep.Mapping]: SetupStep.Mapping, + [SetupStep.Review]: SetupStep.Review, + }, + }, + [SetupStep.Metadata]: { + on: { + back: SetupStep.Ingest, + next: SetupStep.Geocoding, + [SetupStep.Ingest]: SetupStep.Ingest, + [SetupStep.Metadata]: SetupStep.Metadata, + [SetupStep.Geocoding]: SetupStep.Geocoding, + [SetupStep.Mapping]: SetupStep.Mapping, + [SetupStep.Review]: SetupStep.Review, + }, + }, + [SetupStep.Geocoding]: { + on: { + back: SetupStep.Metadata, + next: SetupStep.Mapping, + [SetupStep.Ingest]: SetupStep.Ingest, + [SetupStep.Metadata]: SetupStep.Metadata, + [SetupStep.Geocoding]: SetupStep.Geocoding, + [SetupStep.Mapping]: SetupStep.Mapping, + [SetupStep.Review]: SetupStep.Review, + }, + }, + [SetupStep.Mapping]: { + on: { + back: SetupStep.Geocoding, + next: SetupStep.Review, + [SetupStep.Ingest]: SetupStep.Ingest, + [SetupStep.Metadata]: SetupStep.Metadata, + [SetupStep.Geocoding]: SetupStep.Geocoding, + [SetupStep.Mapping]: SetupStep.Mapping, + [SetupStep.Review]: SetupStep.Review, + }, + }, + [SetupStep.Review]: { + on: { + back: SetupStep.Mapping, + [SetupStep.Ingest]: SetupStep.Ingest, + [SetupStep.Metadata]: SetupStep.Metadata, + [SetupStep.Geocoding]: SetupStep.Geocoding, + [SetupStep.Mapping]: SetupStep.Mapping, + [SetupStep.Review]: SetupStep.Review, + }, + }, + }, + }) + +export const configureDataSourceStateAtom = atomWithMachine(() => + createConfigureDataSourceStateMachine() +) diff --git a/nextjs/src/lib/data.ts b/nextjs/src/lib/data.ts index 1f7387e76..2f01c508b 100644 --- a/nextjs/src/lib/data.ts +++ b/nextjs/src/lib/data.ts @@ -29,7 +29,7 @@ export const externalDataSourceOptions: Record< key: CrmType modelName: string name: string - icon?: ({ className }: { className?: string | undefined }) => any + icon: ({ className }: { className?: string | undefined }) => any logo: ({ className }: { className?: string | undefined }) => any screenshot: string supported: boolean diff --git a/nextjs/tsconfig.json b/nextjs/tsconfig.json index 1cf6871c4..e14a69379 100644 --- a/nextjs/tsconfig.json +++ b/nextjs/tsconfig.json @@ -1,6 +1,8 @@ { "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], + // `strictNullChecks` requested by XState - https://stately.ai/docs/typescript#set-up-your-tsconfigjson-file + "strictNullChecks": true, "allowJs": true, "skipLibCheck": true, "strict": true,