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

feat(web): select primary integration modal #3869

Merged
merged 15 commits into from
Aug 8, 2023

Conversation

LetItRock
Copy link
Contributor

@LetItRock LetItRock commented Jul 27, 2023

What change does this PR introduce?

Implemented the Select Primary Integration modal that will be shown when after the user creates the integration if there are already any integrations for the same env and channel.

Why was this change needed?

Multi-Channel Provider PRD

Other information (Screenshots)

Screenshot 2023-07-27 at 18 17 34
Screenshot 2023-07-27 at 18 18 36
Screenshot 2023-07-27 at 18 18 16
Screenshot 2023-07-27 at 18 18 08
Screenshot 2023-07-27 at 18 18 01

Screenshot 2023-08-02 at 13 14 20
Screenshot 2023-08-02 at 13 14 08
Screenshot 2023-08-02 at 13 13 57

@LetItRock LetItRock self-assigned this Jul 27, 2023
@linear
Copy link

linear bot commented Jul 27, 2023

import { successMessage } from '../../utils/notifications';
import { QueryKeys } from '../query.keys';

export const useMakePrimaryIntegration = (
Copy link
Contributor Author

@LetItRock LetItRock Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the hook that is used when the user clicks the "Make primary" button in the modal...

@@ -21,6 +21,7 @@ interface IButtonProps extends ButtonProps {
pulse?: boolean;
sx?: Sx;
iconPosition?: 'left' | 'right';
testId?: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed the warning in the console

border: 1px solid ${colors.gradientMiddle};
`;

export const Star = (props: React.ComponentPropsWithoutRef<'svg'>) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The star icon, I don't know why but when I export the whole "Primary Provider Icon" from Figma - the icon doesn't have a correct viewport, that's why I just exported the star and made the wrapper circle by hands... I will ask Nikolay to take a look

Comment on lines +19 to +20
paddingLeft: withSelection ? 10 : 30,
paddingRight: withSelection ? 10 : 30,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the styles for the table when it has an option to select a row... more about it below

Comment on lines +57 to +61
withSelection?: boolean;
withRowClickSelection?: boolean;
initialSelectedRows?: Record<IdType<T>, boolean>;
onRowClick?: (row: Row<T>) => void;
onRowSelect?: (row: Row<T>) => void;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the functionality to the table that will allow us to have a radio button in the first column of the table. Only one row could be selected and a callback triggered on selection.

Comment on lines 146 to 150
const initialSelectedIndex = useMemo(() => {
const primary = findPrimaryIntegration(integrationsByEnvAndChannel ?? []);

return integrationsByEnvAndChannel?.indexOf(primary);
}, [integrationsByEnvAndChannel]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find the initial index for a primary integration which will be selected in the modal

Comment on lines 157 to 163
const onRowSelectCallback = (row: Row<ITableIntegration>) => {
setSelectedState({
selectedIntegrationId: row.original.integrationId,
isActive: row.original.active,
selectedRowId: row.id,
});
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the integration is selected by clicking on radio button we do store the info in the state

Comment on lines +213 to +225
<When truthy={isLoading}>
<Table loading loadingItems={5} data-test-id="integrations-list-table" columns={columns} withSelection />
</When>
<When truthy={!isLoading}>
<Table
data-test-id="integrations-list-table"
columns={columns}
data={integrationsByEnvAndChannel}
withSelection
onRowSelect={onRowSelectCallback}
initialSelectedRows={initialSelectedIndex > -1 ? { [initialSelectedIndex]: true } : undefined}
/>
</When>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the trick to have a initially selected row... in the beginning, we don't have data for integrations, so we cant calculate the selected row index... if we would set it to -1 in the begging then after the data arrives that index value would be different ex. 1, but the state won't be updated as it's just used for the init...

that's why we do show one table on loading and another when the data is ready...

Comment on lines 164 to 169
useEffectOnce(() => {
openSelectPrimaryIntegrationModal({
environmentId: selectedProvider?.environmentId,
channelType: selectedProvider?.channel,
});
}, hasToSelectPrimaryProvider && !!selectedProvider);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show the modal when we have a hasToSelectPrimaryProvider flag set and the integration is loaded

@@ -32,6 +18,8 @@ export const mapToTableIntegration = (

return {
name: integration.name ?? provider?.displayName,
isPrimary: integration.isPrimary,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this flag should be returned from the BE to mark the integration as primary

@LetItRock LetItRock changed the title feat(web): select primary integration modal [WIP] 🚧 feat(web): select primary integration modal Jul 28, 2023
@LetItRock LetItRock changed the title [WIP] 🚧 feat(web): select primary integration modal feat(web): select primary integration modal Aug 2, 2023
Comment on lines +3 to +12
export const StarEmpty = (props: React.ComponentPropsWithoutRef<'svg'>) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 13" width={18} height={18} {...props}>
<path
fill="#fff"
d="M4.9 9.883 7 8.617 9.1 9.9l-.55-2.4 1.85-1.6-2.433-.217-.966-2.266-.967 2.25-2.433.216L5.45 7.5 4.9 9.883Zm-2.016 2.784 1.083-4.684-3.633-3.15 4.8-.416L7.001 0l1.866 4.417 4.8.416-3.633 3.15 1.083 4.684-4.116-2.484-4.117 2.484Z"
/>
</svg>
);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another different star shown in the mark as primary action

Comment on lines +78 to +99
<Popover
opened={isPopoverOpened && original.primary}
withArrow
withinPortal
offset={10}
transitionDuration={300}
position="top"
width={230}
styles={{ dropdown: { minHeight: 'initial !important' } }}
content={
<Description>
{`The primary provider instance within the ${original.channel.toLowerCase()} ` +
`channel in the ${original.environment.toLowerCase()} environment.`}
</Description>
}
target={
<IconHolder onMouseEnter={() => setPopoverOpened(true)} onMouseLeave={() => setPopoverOpened(false)}>
<Image src={original.logoFileName[`${colorScheme}`]} alt={original.name} />
{original.primary && <Star />}
</IconHolder>
}
/>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the popover shown over the integration icon when it's primary

Comment on lines 44 to 68
const onDeleteClick = () => {
if (!provider) {
return;
}

if (provider.primary) {
openModal({
environmentId: provider.environmentId,
channelType: provider.channel,
onClose: () => {
deleteIntegration({
id: provider.integrationId,
name: provider.name || provider.displayName,
});
},
});

return;
}

deleteIntegration({
id: provider.integrationId,
name: provider.name || provider.displayName,
});
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the delete integration dropdown action is clicked we do check if the integration was primary, and if yes then the user has to pick another primary integration

Comment on lines +105 to +115
{canMarkAsPrimary && (
<Dropdown.Item
onClick={() => {
makePrimaryIntegration({ id: provider.integrationId });
}}
icon={<StarEmpty />}
disabled={isLoading || isMarkingPrimary}
>
Mark as primary
</Dropdown.Item>
)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only email/sms integrations could be marked as primary and also the integration that is not already primary ;)

Comment on lines +146 to +153
const initialSelectedIndex = useMemo(() => {
const primary = integrationsByEnvAndChannel.find((el) => el.primary);
if (primary) {
return integrationsByEnvAndChannel.indexOf(primary ?? {});
}

return -1;
}, [integrationsByEnvAndChannel]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculate the index of default selected integration, it will be the one that is a primary

Comment on lines 166 to 195
const updateAndSelectPrimaryIntegration = async (data: IConstructIntegrationDto) => {
if (!selectedProvider) {
return;
}

const { channel: selectedChannel, environmentId, primary } = selectedProvider;
const isActiveFieldChanged = dirtyFields.active;
const hasSameChannelActiveIntegration = !!providers
.filter((el) => !NOVU_PROVIDERS.includes(el.providerId))
.find((el) => el.active && el.channel === selectedChannel && el.environmentId === environmentId);
const isChannelSupportPrimary = CHANNELS_WITH_PRIMARY.includes(selectedChannel);

if (
isActiveFieldChanged &&
isChannelSupportPrimary &&
((isActive && hasSameChannelActiveIntegration) || (!isActive && primary))
) {
openSelectPrimaryIntegrationModal({
environmentId: selectedProvider?.environmentId,
channelType: selectedProvider?.channel,
onClose: () => {
updateIntegration(data);
},
});

return;
}

updateIntegration(data);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the update integration button is clicked we do check if:

  • integration active field has changed and the integration channel supports primary (email/sms)
  • and is active and there are other active for the same channel
  • or is not active and is a primary

then we do show the modal to pick a new primary

Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small question in this cycle we did not want to touch the workflow providers tab? asking regarding the star in the provider list there.

Do we need to present the deleted provider in the modal?
https://www.loom.com/share/f68a5a64702e4232973d85b02056bcd5

i am not sure if this state is possible but, another thing i encountered in the empty state where i could not add new provider, i was either redirected to workflow page or i was missing the create sidebar.
could be something in my machine tho, the flickering, in the end, is page refresh
https://www.loom.com/share/36a0bf4fea8640f782fec1770fde08f5

Another possible not possible state is the following
https://www.loom.com/share/68da5da165ce4bcc9721927ce5d30904
if it is possible should show the modal at all?

the thing i did not had the time to check is analytics and backward compatibility.

other than that your code looks as clean as always ✨

@LetItRock
Copy link
Contributor Author

small question in this cycle we did not want to touch the workflow providers tab? asking regarding the star in the provider list there.

no in the next cycle, we won't work on the provider's tab

Do we need to present the deleted provider in the modal? https://www.loom.com/share/f68a5a64702e4232973d85b02056bcd5

yes, totally agree with you, I'll exclude it from the modal

i am not sure if this state is possible but, another thing i encountered in the empty state where i could not add new provider, i was either redirected to workflow page or i was missing the create sidebar. could be something in my machine tho, the flickering, in the end, is page refresh https://www.loom.com/share/36a0bf4fea8640f782fec1770fde08f5

I will investigate this case, it's weird.

Another possible not possible state is the following https://www.loom.com/share/68da5da165ce4bcc9721927ce5d30904 if it is possible should show the modal at all?

yes, I will fix this one too

the thing i did not had the time to check is analytics and backward compatibility.

We totally missed the analytics for the multi-channel provider's feature and for the new integrations list page, thank you for noticing that, I'll add it to our backlog.

other than that your code looks as clean as always ✨

thank you @djabarovgeorge for these findings and all your time spend on this feature 🙂 you really helped me a lot! 🤝

LetItRock added a commit that referenced this pull request Aug 7, 2023
@LetItRock
Copy link
Contributor Author

@djabarovgeorge all the things you've found have been fixed :)

Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

the last thing that is left for us is the integrations related tests that are failing.

@LetItRock LetItRock added this pull request to the merge queue Aug 8, 2023
Merged via the queue into next with commit d5303b3 Aug 8, 2023
30 checks passed
@LetItRock LetItRock deleted the nv-2606-select-primary-integration-modal branch August 8, 2023 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants