From 8287075e299f2888f07c2db5f943ea6fa1fed863 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Thu, 15 Jun 2023 10:52:39 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A7=20Improve=20SMTP=20event=20section?= =?UTF-8?q?=20(#548)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make channels section expandable based on override setting * Revert "Make channels section expandable based on override setting" This reverts commit e107c5e990b4110156043ed494fb0054bd936654. * Improve copy in the descriptions * Handle partial updates i n events * Add status component * Fix typos and types * Improve SMTP events section * Add changeset * Implement event sections as tables with array form * Update the changelog * Remove no longer used component * Add empty option for template choice * Remove no longer used component * Update the test --- .changeset/young-tigers-provide.md | 5 + .../src/components/table.tsx | 12 ++ .../configuration-name-description-text.tsx | 3 +- .../event-handlers/message-event-types.ts | 2 +- .../sendgrid-config-input-schema.ts | 7 + .../sendgrid-configuration.router.ts | 51 +++--- .../sendgrid-configuration.service.test.ts | 2 + .../sendgrid-configuration.service.ts | 17 +- .../sendgrid/ui/sendgrid-events-section.tsx | 162 ++++++++--------- .../configuration/smtp-config-input-schema.ts | 22 +++ .../smtp-configuration.router.ts | 70 +++++--- .../smtp-configuration.service.test.ts | 2 + .../smtp-configuration.service.ts | 17 +- .../ui/{code-edtor.tsx => code-editor.tsx} | 0 .../src/modules/smtp/ui/event-form.tsx | 25 ++- .../modules/smtp/ui/smtp-events-section.tsx | 166 ++++++++++-------- .../[configurationId]/event/[eventType].tsx | 5 +- 17 files changed, 339 insertions(+), 229 deletions(-) create mode 100644 .changeset/young-tigers-provide.md create mode 100644 apps/emails-and-messages/src/components/table.tsx rename apps/emails-and-messages/src/modules/smtp/ui/{code-edtor.tsx => code-editor.tsx} (100%) diff --git a/.changeset/young-tigers-provide.md b/.changeset/young-tigers-provide.md new file mode 100644 index 0000000..a3b33f6 --- /dev/null +++ b/.changeset/young-tigers-provide.md @@ -0,0 +1,5 @@ +--- +"saleor-app-emails-and-messages": patch +--- + +Events section UI has been updated. All events are displayed now as single table. diff --git a/apps/emails-and-messages/src/components/table.tsx b/apps/emails-and-messages/src/components/table.tsx new file mode 100644 index 0000000..8ee9ea7 --- /dev/null +++ b/apps/emails-and-messages/src/components/table.tsx @@ -0,0 +1,12 @@ +import { Box, BoxProps } from "@saleor/macaw-ui/next"; + +export const Table = { + Container: (props: BoxProps) => , + Header: (props: BoxProps) => , + Row: (props: BoxProps) => , + HeaderCell: (props: BoxProps) => ( + + ), + Body: (props: BoxProps) => , + Cell: (props: BoxProps) => , +}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/configuration-name-description-text.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/configuration-name-description-text.tsx index 66dc00b..35edb5f 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/configuration-name-description-text.tsx +++ b/apps/emails-and-messages/src/modules/app-configuration/ui/configuration-name-description-text.tsx @@ -2,7 +2,8 @@ import { Text } from "@saleor/macaw-ui/next"; export const ConfigurationNameDescriptionText = () => ( - The name for your configuration. You can have more than one if you want to use different settings for each channel. + The name for your configuration. You can have more than one if you want to use different + settings for each channel.
For example - production and development.
diff --git a/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts b/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts index 7a51c15..1d0c589 100644 --- a/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts +++ b/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts @@ -22,7 +22,7 @@ export const messageEventTypesLabels: Record = { ORDER_FULLY_PAID: "Order fully paid", INVOICE_SENT: "Invoice sent", ACCOUNT_CONFIRMATION: "Customer account confirmation", - ACCOUNT_PASSWORD_RESET: "Customer account password reset", + ACCOUNT_PASSWORD_RESET: "Customer account password reset request", ACCOUNT_CHANGE_EMAIL_REQUEST: "Customer account change email request", ACCOUNT_CHANGE_EMAIL_CONFIRM: "Customer account change email confirmation", ACCOUNT_DELETE: "Customer account delete request", diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts index abc8054..de4a492 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts @@ -86,3 +86,10 @@ export const sendgridUpdateEventSchema = sendgridConfigurationEventSchema.merge( ); export type SendgridUpdateEvent = z.infer; + +export const sendgridUpdateEventArraySchema = z.object({ + configurationId: z.string(), + events: z.array(sendgridConfigurationEventSchema), +}); + +export type SendgridUpdateEventArray = z.infer; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.router.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.router.ts index 184dcbc..5c4231a 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.router.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.router.ts @@ -6,7 +6,7 @@ import { sendgridGetEventConfigurationInputSchema, sendgridUpdateApiConnectionSchema, sendgridUpdateBasicInformationSchema, - sendgridUpdateEventConfigurationInputSchema, + sendgridUpdateEventArraySchema, sendgridUpdateEventSchema, sendgridUpdateSenderSchema, } from "./sendgrid-config-input-schema"; @@ -147,23 +147,6 @@ export const sendgridConfigurationRouter = router({ throwTrpcErrorFromConfigurationServiceError(e); } }), - updateEventConfiguration: protectedWithConfigurationService - .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) - .input(sendgridUpdateEventConfigurationInputSchema) - .mutation(async ({ ctx, input }) => { - const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); - - logger.debug(input, "sendgridConfigurationRouter.updateEventConfiguration called"); - - try { - return await ctx.sendgridConfigurationService.updateEventConfiguration({ - configurationId: input.configurationId, - eventConfiguration: input, - }); - } catch (e) { - throwTrpcErrorFromConfigurationServiceError(e); - } - }), updateBasicInformation: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(sendgridUpdateBasicInformationSchema) @@ -260,13 +243,41 @@ export const sendgridConfigurationRouter = router({ logger.debug(input, "sendgridConfigurationRouter.updateEvent called"); + const { id: configurationId, eventType, ...eventConfiguration } = input; + try { return await ctx.sendgridConfigurationService.updateEventConfiguration({ - eventConfiguration: input, - configurationId: input.id, + configurationId, + eventType, + eventConfiguration, }); } catch (e) { throwTrpcErrorFromConfigurationServiceError(e); } }), + + updateEventArray: protectedWithConfigurationService + .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) + .input(sendgridUpdateEventArraySchema) + .mutation(async ({ ctx, input }) => { + const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); + + logger.debug(input, "sendgridConfigurationRouter.updateEventArray called"); + + return await Promise.all( + input.events.map(async (event) => { + const { eventType, ...eventConfiguration } = event; + + try { + return await ctx.sendgridConfigurationService.updateEventConfiguration({ + configurationId: input.configurationId, + eventType, + eventConfiguration, + }); + } catch (e) { + throwTrpcErrorFromConfigurationServiceError(e); + } + }) + ); + }), }); diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.test.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.test.ts index ace2eeb..2856e58 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.test.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.test.ts @@ -512,6 +512,7 @@ describe("SendgridConfigurationService", function () { await service.updateEventConfiguration({ configurationId: validConfig.configurations[0].id, + eventType: validConfig.configurations[0].events[0].eventType, eventConfiguration: { ...validConfig.configurations[0].events[0], template: "42", @@ -544,6 +545,7 @@ describe("SendgridConfigurationService", function () { await expect(async () => service.updateEventConfiguration({ configurationId: "this-id-does-not-exist", + eventType: validConfig.configurations[0].events[0].eventType, eventConfiguration: { ...validConfig.configurations[0].events[0], template: "42", diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.ts index 35e13e0..bd693de 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configuration.service.ts @@ -234,19 +234,19 @@ export class SendgridConfigurationService { */ async updateEventConfiguration({ configurationId, + eventType, eventConfiguration, }: { configurationId: string; - eventConfiguration: SendgridEventConfiguration; + eventType: SendgridEventConfiguration["eventType"]; + eventConfiguration: Partial>; }) { logger.debug("Update event configuration"); const configuration = await this.getConfiguration({ id: configurationId, }); - const eventIndex = configuration.events.findIndex( - (e) => e.eventType === eventConfiguration.eventType - ); + const eventIndex = configuration.events.findIndex((e) => e.eventType === eventType); if (eventIndex < 0) { logger.warn("Event configuration not found, throwing an error"); @@ -256,9 +256,14 @@ export class SendgridConfigurationService { ); } - configuration.events[eventIndex] = eventConfiguration; + const updatedEventConfiguration = { + ...configuration.events[eventIndex], + ...eventConfiguration, + }; + + configuration.events[eventIndex] = updatedEventConfiguration; await this.updateConfiguration(configuration); - return configuration; + return updatedEventConfiguration; } } diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx index 8816013..dd91cd2 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx +++ b/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx @@ -8,8 +8,8 @@ import { defaultPadding } from "../../../components/ui-defaults"; import { useDashboardNotification } from "@saleor/apps-shared"; import { trpcClient } from "../../trpc/trpc-client"; import { - SendgridUpdateEvent, - sendgridUpdateEventSchema, + SendgridUpdateEventArray, + sendgridUpdateEventArraySchema, } from "../configuration/sendgrid-config-input-schema"; import { useForm } from "react-hook-form"; import { BoxFooter } from "../../../components/box-footer"; @@ -18,91 +18,57 @@ import { useQuery } from "@tanstack/react-query"; import { fetchTemplates } from "../sendgrid-api"; import { zodResolver } from "@hookform/resolvers/zod"; import { setBackendErrors } from "../../../lib/set-backend-errors"; -import { Combobox } from "@saleor/react-hook-form-macaw"; +import { Select } from "@saleor/react-hook-form-macaw"; import { TextLink } from "@saleor/apps-ui"; - -interface EventBoxProps { - configuration: SendgridConfiguration; - event: SendgridEventConfiguration; -} - -const EventBox = ({ event, configuration }: EventBoxProps) => { - const { notifySuccess, notifyError } = useDashboardNotification(); - - const { data: templatesChoices } = useQuery({ - queryKey: ["sendgridTemplates"], - queryFn: fetchTemplates({ apiKey: configuration.apiKey }), - enabled: !!configuration.apiKey?.length, - }); - - const { handleSubmit, control, setError, register } = useForm({ - defaultValues: { - id: configuration.id, - ...event, - }, - resolver: zodResolver(sendgridUpdateEventSchema), - }); - - const trpcContext = trpcClient.useContext(); - const { mutate } = trpcClient.sendgridConfiguration.updateEvent.useMutation({ - onSuccess: async () => { - notifySuccess("Configuration saved"); - trpcContext.sendgridConfiguration.invalidate(); - }, - onError(error) { - setBackendErrors({ error, setError, notifyError }); - }, - }); - - return ( -
{ - mutate({ - ...data, - }); - })} - > - - - {event.eventType} - {templatesChoices?.length ? ( - ({ - label: sender.label, - value: sender.value, - }))} - /> - ) : ( - - )} - - - - - - - -
- ); -}; +import { messageEventTypesLabels } from "../../event-handlers/message-event-types"; +import { Table } from "../../../components/table"; interface SendgridEventsSectionProps { configuration: SendgridConfiguration; } export const SendgridEventsSection = ({ configuration }: SendgridEventsSectionProps) => { + const { notifySuccess, notifyError } = useDashboardNotification(); + + // Sort events by displayed label + const eventsSorted = configuration.events.sort((a, b) => + messageEventTypesLabels[a.eventType].localeCompare(messageEventTypesLabels[b.eventType]) + ); + + const { control, register, handleSubmit, setError } = useForm({ + defaultValues: { + configurationId: configuration.id, + events: eventsSorted, + }, + resolver: zodResolver(sendgridUpdateEventArraySchema), + }); + + const trpcContext = trpcClient.useContext(); + const { mutate } = trpcClient.sendgridConfiguration.updateEventArray.useMutation({ + onSuccess: async () => { + notifySuccess("Configuration saved"); + trpcContext.sendgridConfiguration.invalidate(); + }, + onError(error) { + setBackendErrors({ error, setError, notifyError }); + }, + }); + + const { data: sendgridTemplates } = useQuery({ + queryKey: ["sendgridTemplates"], + queryFn: fetchTemplates({ apiKey: configuration.apiKey }), + enabled: !!configuration.apiKey?.length, + }); + + const templateChoices = [{ value: "", label: "----" }, ...(sendgridTemplates || [])]; + return ( - Choose which Saleor events should send emails via Sendgrid. You can create and modify your templates in the + Choose which Saleor events should send emails via Sendgrid. You can create and modify your + templates in the Sendgrid dashboard @@ -110,11 +76,47 @@ export const SendgridEventsSection = ({ configuration }: SendgridEventsSectionPr } > - - {configuration.events.map((event) => ( - - ))} - +
{ + mutate(data); + })} + > + + + + + + Active + Event type + Dynamic template + + + + {eventsSorted.map((event, index) => ( + + + + + + {messageEventTypesLabels[event.eventType]} + + + - Active - - - - - - -
- ); -}; interface SmtpEventsSectionProps { configuration: SmtpConfiguration; } export const SmtpEventsSection = ({ configuration }: SmtpEventsSectionProps) => { + const { notifySuccess, notifyError } = useDashboardNotification(); + const router = useRouter(); + + // Sort events by displayed label + const eventsSorted = configuration.events.sort((a, b) => + messageEventTypesLabels[a.eventType].localeCompare(messageEventTypesLabels[b.eventType]) + ); + + const { register, handleSubmit, setError } = useForm({ + defaultValues: { + configurationId: configuration.id, + events: eventsSorted, + }, + resolver: zodResolver(smtpUpdateEventArraySchema), + }); + + const trpcContext = trpcClient.useContext(); + const { mutate } = trpcClient.smtpConfiguration.updateEventArray.useMutation({ + onSuccess: async () => { + notifySuccess("Configuration saved"); + trpcContext.smtpConfiguration.invalidate(); + }, + onError(error) { + setBackendErrors({ error, setError, notifyError }); + }, + }); + return (
} > - - {configuration.events.map((event) => ( - - ))} - +
{ + mutate(data); + })} + > + + + + + + Active + Event type + + + + + {eventsSorted.map((event, index) => ( + + + + + + {messageEventTypesLabels[event.eventType]} + + + + + + ))} + + + + + + + +
); }; diff --git a/apps/emails-and-messages/src/pages/configuration/smtp/[configurationId]/event/[eventType].tsx b/apps/emails-and-messages/src/pages/configuration/smtp/[configurationId]/event/[eventType].tsx index f962477..0879058 100644 --- a/apps/emails-and-messages/src/pages/configuration/smtp/[configurationId]/event/[eventType].tsx +++ b/apps/emails-and-messages/src/pages/configuration/smtp/[configurationId]/event/[eventType].tsx @@ -9,6 +9,7 @@ import { appUrls } from "../../../../../modules/app-configuration/urls"; import { EventForm } from "../../../../../modules/smtp/ui/event-form"; import { smtpUrls } from "../../../../../modules/smtp/urls"; import { TextLink } from "@saleor/apps-ui"; +import { messageEventTypesLabels } from "../../../../../modules/event-handlers/message-event-types"; const LoadingView = () => { return ( @@ -78,12 +79,12 @@ const EditSmtpEventPage: NextPage = () => { breadcrumbs={[ { name: "Configuration", href: appUrls.configuration() }, { name: `SMTP: ${configuration.name}`, href: smtpUrls.configuration(configurationId) }, - { name: eventType }, + { name: messageEventTypesLabels[eventType] }, ]} > - Edit template for {eventType} event. You can learn more about MJML{" "} + Edit template for {eventType} event. You can learn more about MJML{" "} here