Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preferences - Connect Services Page #820

Merged
merged 55 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
722be7b
Adding shared forms that we use on the Preferences
dr-bizz Nov 10, 2023
6f8940c
Fixing small errors and combining tests
dr-bizz Nov 13, 2023
7728d54
Merge branch 'main' into preferences-connect-services
dr-bizz Nov 13, 2023
55165f8
Creating page and context
dr-bizz Nov 13, 2023
2881f90
Helper functions also added variables to helpscout
dr-bizz Nov 13, 2023
4fda0ee
GraphQL Proxys needed for functionailty.
dr-bizz Nov 13, 2023
d4ae985
The Key Integration
dr-bizz Nov 13, 2023
878959d
Previously changed integrations page. Now fixing URL issue.
dr-bizz Nov 13, 2023
0a32381
The Oragnization integration
dr-bizz Nov 13, 2023
aac4426
Organization Helper functions
dr-bizz Nov 13, 2023
4c860f6
The key Image
dr-bizz Nov 13, 2023
212c0ef
The Google Integration
dr-bizz Nov 13, 2023
e9e3b98
The Mailchimp Integration
dr-bizz Nov 13, 2023
815beef
The PrayerLetter Integration
dr-bizz Nov 13, 2023
0a4b9e9
The Chalkline Integration
dr-bizz Nov 13, 2023
6f63002
2 test broke on Github but worked on local. Hoping this will fix them
dr-bizz Nov 14, 2023
6d7f3a0
Ensure sync test is working
dr-bizz Nov 14, 2023
60c57fd
Capitlaized IntegrationsContext
dr-bizz Nov 15, 2023
27c88ca
Renaming to something random so GIT picks up on the case change
dr-bizz Nov 15, 2023
8f2fd59
Capitial I in IntegrationsContext.tsx
dr-bizz Nov 15, 2023
4fcec99
Moving query.selectTab into the component.
dr-bizz Nov 15, 2023
c1613ad
Removing unused type
dr-bizz Nov 15, 2023
04e1d2d
Switching accountListID to accountListId
dr-bizz Nov 15, 2023
afb2cd5
Removing 'get' from GraphQL name
dr-bizz Nov 15, 2023
35c4d3e
.map to .forEach
dr-bizz Nov 15, 2023
03ff6c4
getMailchimpAccount - to - mailchimpAccount
dr-bizz Nov 15, 2023
e3978b4
getPrayerlettersAccount - to - PrayerlettersAccount
dr-bizz Nov 15, 2023
aa016f2
forEach
dr-bizz Nov 15, 2023
e811a0d
Google amendments
dr-bizz Nov 16, 2023
34af2d7
Fixing Lint issue with previous MailChimp change
dr-bizz Nov 16, 2023
26ed75b
MailChimp amendments
dr-bizz Nov 16, 2023
cbafc02
Changing Accordian to Accordion
dr-bizz Nov 16, 2023
70e4243
Fixing MailChimp lint issue
dr-bizz Nov 16, 2023
71a5e7d
Switching mpdx to appName
dr-bizz Nov 16, 2023
7fc1c62
Organization amendments
dr-bizz Nov 16, 2023
f3dfcfd
PrayerLetter amendments
dr-bizz Nov 16, 2023
05d2fd8
Odd amendments that I previously missed
dr-bizz Nov 16, 2023
6c46a67
Adding tests to Organization data sync upload file - Needed more tests
dr-bizz Nov 17, 2023
d26c6a6
simplifing code
dr-bizz Nov 20, 2023
f39f9b8
Adding tests for helpscout and env var to globalSetup for test
dr-bizz Nov 20, 2023
bd4919f
Adding tests to sync google integration
dr-bizz Nov 20, 2023
eee6562
Removing leftover code
dr-bizz Nov 28, 2023
bb2837a
Removing Components()
dr-bizz Nov 28, 2023
9e640d1
Localized edits
dr-bizz Nov 28, 2023
8ba9cb9
Small fixes
dr-bizz Nov 28, 2023
ae2ee65
Test fixes
dr-bizz Nov 28, 2023
3526957
Added Id for graphQL cache
dr-bizz Nov 28, 2023
cf204bb
Button styles and margins
dr-bizz Nov 29, 2023
82dff5c
Lint fixes
dr-bizz Nov 29, 2023
74a4712
Helpscout test fixes
dr-bizz Nov 29, 2023
3fef441
Making Helpscout changes so it's easier to merge with Caleb C's PRs
dr-bizz Nov 29, 2023
a6f5572
Helpscout - Fixing test errors
dr-bizz Nov 29, 2023
74ad75b
Merge branch 'main' into preferences-connect-services-2
dr-bizz Nov 30, 2023
006d4b7
Testing Chalkline opens new tab
dr-bizz Nov 30, 2023
a09a6c6
Added article export after merge
dr-bizz Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ module.exports = withPlugins([
API_URL: process.env.API_URL ?? 'https://api.stage.mpdx.org/graphql',
REST_API_URL:
process.env.REST_API_URL ?? 'https://api.stage.mpdx.org/api/v2/',
OAUTH_URL: process.env.OAUTH_URL ?? 'https://auth.stage.mpdx.org',
SITE_URL: siteUrl,
CLIENT_ID: process.env.CLIENT_ID ?? '4027334344069527005',
CLIENT_SECRET: process.env.CLIENT_SECRET,
Expand Down Expand Up @@ -104,6 +105,9 @@ module.exports = withPlugins([
HS_HOME_SUGGESTIONS: process.env.HS_HOME_SUGGESTIONS,
HS_REPORTS_SUGGESTIONS: process.env.HS_REPORTS_SUGGESTIONS,
HS_TASKS_SUGGESTIONS: process.env.HS_TASKS_SUGGESTIONS,
HS_SETTINGS_SERVICES_SUGGESTIONS:
process.env.HS_SETTINGS_SERVICES_SUGGESTIONS,
HS_SETUP_FIND_ORGANIZATION: process.env.HS_SETUP_FIND_ORGANIZATION,
ALERT_MESSAGE: process.env.ALERT_MESSAGE,
},
experimental: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

export type IntegrationsContextType = {
apiToken: string;
};
export const IntegrationsContext =
React.createContext<IntegrationsContextType | null>(null);

interface IntegrationsContextProviderProps {
children: React.ReactNode;
apiToken: string;
}
export const IntegrationsContextProvider: React.FC<
IntegrationsContextProviderProps
> = ({ children, apiToken }) => {
return (
<IntegrationsContext.Provider value={{ apiToken }}>
{children}
</IntegrationsContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { ReactElement, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { GetServerSideProps } from 'next';
import { getToken } from 'next-auth/jwt';
import { useRouter } from 'next/router';
import { suggestArticles } from 'src/lib/helpScout';
import { AccordionGroup } from 'src/components/Shared/Forms/Accordions/AccordionGroup';
import { TheKeyAccordion } from 'src/components/Settings/integrations/Key/TheKeyAccordion';
import { OrganizationAccordion } from 'src/components/Settings/integrations/Organization/OrganizationAccordion';
import { GoogleAccordion } from 'src/components/Settings/integrations/Google/GoogleAccordion';
import { MailchimpAccordion } from 'src/components/Settings/integrations/Mailchimp/MailchimpAccordion';
import { PrayerlettersAccordion } from 'src/components/Settings/integrations/Prayerletters/PrayerlettersAccordion';
import { ChalklineAccordion } from 'src/components/Settings/integrations/Chalkline/ChalklineAccordion';
import { SettingsWrapper } from '../wrapper';
import { IntegrationsContextProvider } from './IntegrationsContext';

interface Props {
apiToken: string;
}

const Integrations = ({ apiToken }: Props): ReactElement => {
const { t } = useTranslation();
const { query } = useRouter();
const [expandedPanel, setExpandedPanel] = useState(
(query?.selectedTab as string) || '',
);

useEffect(() => {
suggestArticles('HS_SETTINGS_SERVICES_SUGGESTIONS');
}, []);

const handleAccordionChange = (panel: string) => {
const panelLowercase = panel.toLowerCase();
setExpandedPanel(expandedPanel === panelLowercase ? '' : panelLowercase);
};

return (
<SettingsWrapper
pageTitle={t('Connect Services')}
pageHeading={t('Connect Services')}
>
<IntegrationsContextProvider apiToken={apiToken}>
<AccordionGroup title="">
<TheKeyAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
<OrganizationAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
</AccordionGroup>
<AccordionGroup title={t('External Services')}>
<GoogleAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
<MailchimpAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
<PrayerlettersAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
<ChalklineAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
/>
</AccordionGroup>
</IntegrationsContextProvider>
</SettingsWrapper>
);
};

export const getServerSideProps: GetServerSideProps = async ({ req }) => {
const jwtToken = (await getToken({
req,
secret: process.env.JWT_SECRET as string,
})) as { apiToken: string } | null;
const apiToken = jwtToken?.apiToken;

return {
props: {
apiToken,
},
};
};

export default Integrations;
74 changes: 74 additions & 0 deletions pages/accountLists/[accountListId]/settings/wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Box, Container } from '@mui/material';
import { styled } from '@mui/material/styles';
import React, { useState } from 'react';
import Head from 'next/head';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout';
import {
MultiPageMenu,
NavTypeEnum,
} from 'src/components/Shared/MultiPageLayout/MultiPageMenu/MultiPageMenu';
import {
MultiPageHeader,
HeaderTypeEnum,
} from 'src/components/Shared/MultiPageLayout/MultiPageHeader';

const PageContentWrapper = styled(Container)(({ theme }) => ({
paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(3),
}));

interface SettingsWrapperProps {
pageTitle: string;
pageHeading: string;
children?: React.ReactNode;
}

export const SettingsWrapper: React.FC<SettingsWrapperProps> = ({
pageTitle,
pageHeading,
children,
}) => {
const { appName } = useGetAppSettings();
const [isNavListOpen, setNavListOpen] = useState(false);
const handleNavListToggle = () => {
setNavListOpen(!isNavListOpen);
};

return (
<>
<Head>
<title>
{appName} | {pageTitle}
</title>
</Head>
<Box component="main">
<SidePanelsLayout
isScrollBox={false}
leftPanel={
<MultiPageMenu
isOpen={isNavListOpen}
selectedId="responsibilityCenters"
onClose={handleNavListToggle}
navType={NavTypeEnum.Settings}
/>
}
leftOpen={isNavListOpen}
leftWidth="290px"
mainContent={
<>
<MultiPageHeader
isNavListOpen={isNavListOpen}
onNavListToggle={handleNavListToggle}
title={pageHeading}
rightExtra={null}
headerType={HeaderTypeEnum.Settings}
/>
<PageContentWrapper maxWidth="lg">{children}</PageContentWrapper>
</>
}
/>
</Box>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const SendToChalkline = (): string => {
return 'success';
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Resolvers } from '../../../../../../graphql-rest.page.generated';

const SendToChalklineResolvers: Resolvers = {
Mutation: {
sendToChalkline: async (
_source,
{ input: { accountListId } },
{ dataSources },
) => {
return dataSources.mpdxRestApi.sendToChalkline(accountListId);
},
},
};

export { SendToChalklineResolvers };
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extend type Mutation {
sendToChalkline(input: SendToChalklineInput!): String!
}

input SendToChalklineInput {
accountListId: ID!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
extend type Mutation {
createGoogleIntegration(
input: CreateGoogleIntegrationInput!
): GoogleAccountIntegration!
}

input CreateGoogleIntegrationInput {
googleAccountId: ID!
googleIntegration: GoogleAccountIntegrationInput
accountListId: String!
}

input GoogleAccountIntegrationInput {
overwrite: Boolean
calendarIntegration: Boolean
calendarId: String
calendarIntegrations: [String]
calendarName: String
calendars: [GoogleAccountIntegrationCalendarsInput]
createdAt: String
updatedAt: String
id: String
updatedInDbAt: String
}

input GoogleAccountIntegrationCalendarsInput {
id: String
name: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { snakeToCamel } from 'src/lib/snakeToCamel';

export interface CreateGoogleIntegrationResponse {
id: string;
type: string;
attributes: Omit<CreateGoogleIntegrationAttributes, 'id'>;
relationships: relationships;
}

type relationships = {
account_list: object[];
google_account: object[];
};

export interface CreateGoogleIntegrationAttributes {
calendar_id: string;
calendar_integration: boolean;
calendar_integrations: string[];
calendar_name: string;
calendars: calendars[];
created_at: string;
updated_at: string;
id: string;
updated_in_db_at: string;
}

interface CreateGoogleIntegrationAttributesCamel {
calendarId: string;
calendarIntegration: boolean;
calendarIntegrations: string[];
calendarName: string;
calendars: calendars[];
createdAt: string;
updatedAt: string;
id: string;
updatedInDbAt: string;
}
type calendars = {
id: string;
name: string;
};

export const CreateGoogleIntegration = (
data: CreateGoogleIntegrationResponse,
): CreateGoogleIntegrationAttributesCamel => {
const attributes = {} as Omit<CreateGoogleIntegrationAttributesCamel, 'id'>;
Object.keys(data.attributes).forEach((key) => {
attributes[snakeToCamel(key)] = data.attributes[key];
});

return {
id: data.id,
...attributes,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Resolvers } from '../../../../../../graphql-rest.page.generated';

const CreateGoogleIntegrationResolvers: Resolvers = {
Mutation: {
createGoogleIntegration: async (
_source,
{ input: { googleAccountId, googleIntegration, accountListId } },
{ dataSources },
) => {
return dataSources.mpdxRestApi.createGoogleIntegration(
googleAccountId,
googleIntegration,
accountListId,
);
},
},
};

export { CreateGoogleIntegrationResolvers };
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type DeleteGoogleAccountResponse = {
success: boolean;
};

export const DeleteGoogleAccount = (): DeleteGoogleAccountResponse => {
return {
success: true,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extend type Mutation {
deleteGoogleAccount(
input: DeleteGoogleAccountInput!
): GoogleAccountDeletionResponse!
}

input DeleteGoogleAccountInput {
accountId: ID!
}

type GoogleAccountDeletionResponse {
success: Boolean!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Resolvers } from '../../../../../../graphql-rest.page.generated';

const DeleteGoogleAccountResolvers: Resolvers = {
Mutation: {
deleteGoogleAccount: async (
_source,
{ input: { accountId } },
{ dataSources },
) => {
return dataSources.mpdxRestApi.deleteGoogleAccount(accountId);
},
},
};

export { DeleteGoogleAccountResolvers };
Loading
Loading