diff --git a/apps/emails-and-messages/package.json b/apps/emails-and-messages/package.json
index aacf571..d21ff8b 100644
--- a/apps/emails-and-messages/package.json
+++ b/apps/emails-and-messages/package.json
@@ -19,7 +19,7 @@
"@monaco-editor/react": "^4.4.6",
"@saleor/app-sdk": "0.37.3",
"@saleor/apps-shared": "workspace:*",
- "@saleor/macaw-ui": "0.8.0-pre.76",
+ "@saleor/macaw-ui": "0.8.0-pre.79",
"@sendgrid/client": "^7.7.0",
"@sendgrid/mail": "^7.7.0",
"@tanstack/react-query": "^4.24.4",
diff --git a/apps/emails-and-messages/src/components/basic-layout.tsx b/apps/emails-and-messages/src/components/basic-layout.tsx
new file mode 100644
index 0000000..52876a3
--- /dev/null
+++ b/apps/emails-and-messages/src/components/basic-layout.tsx
@@ -0,0 +1,17 @@
+import { Box } from "@saleor/macaw-ui/next";
+import { Breadcrumbs } from "./breadcrumbs";
+
+interface BasicLayoutProps {
+ children: React.ReactNode;
+ isLoading?: boolean;
+ breadcrumbs?: { name: string; href?: string }[];
+}
+
+export const BasicLayout = ({ children, breadcrumbs, isLoading = false }: BasicLayoutProps) => {
+ return (
+
+ {breadcrumbs?.length && }
+ {children}
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/breadcrumbs.tsx b/apps/emails-and-messages/src/components/breadcrumbs.tsx
index 661e761..5624656 100644
--- a/apps/emails-and-messages/src/components/breadcrumbs.tsx
+++ b/apps/emails-and-messages/src/components/breadcrumbs.tsx
@@ -1,31 +1,56 @@
import { Box, Text } from "@saleor/macaw-ui/next";
+import Link from "next/link";
+import { TextLink } from "./text-link";
interface BreadcrumbsProps {
items: Array<{ name: string; href?: string }>;
}
+/**
+ * Displays breadcrumbs for the current page.
+ * On desktop full path is visible. On mobile only last item is shown.
+ */
export const Breadcrumbs = (props: BreadcrumbsProps) => {
if (props.items.length === 0) {
return null;
}
- // TODO: do I have to recreate the array here?
- const i = [...props.items];
- const lastItem = i.pop()!; // can enforce the type since array is at least one element long
+ const items = [...props.items];
+ const lastItem = items.pop()!; // can enforce the type since array is at least one element long
return (
- {i.map((item) => (
- <>
-
- {item.name}
-
-
+ {items.map((item) => (
+
+ {!item.href ? (
+
+ {item.name}
+
+ ) : (
+
+ {item.name}
+
+ )}
+
+
{">"}
- >
+
))}
-
+
{lastItem.name}
diff --git a/apps/emails-and-messages/src/components/chip-text.tsx b/apps/emails-and-messages/src/components/chip-text.tsx
new file mode 100644
index 0000000..57d4856
--- /dev/null
+++ b/apps/emails-and-messages/src/components/chip-text.tsx
@@ -0,0 +1,53 @@
+import { Text, Chip, ChipProps } from "@saleor/macaw-ui/next";
+
+interface ChipTextProps {
+ variant?: "default" | "warning" | "error" | "success";
+ content: string;
+}
+
+export const ChipText = ({ variant = "default", content }: ChipTextProps) => {
+ const commonProps: ChipProps = {
+ __maxWidth: "max-content",
+ display: "flex",
+ borderStyle: "solid",
+ borderWidth: 1,
+ };
+
+ // TODO: Choose colors for variants
+ switch (variant) {
+ case "default":
+ return (
+
+
+ {content}
+
+
+ );
+ case "warning":
+ return (
+
+
+ {content}
+
+
+ );
+
+ case "error":
+ return (
+
+
+ {content}
+
+
+ );
+
+ case "success":
+ return (
+
+
+ {content}
+
+
+ );
+ }
+};
diff --git a/apps/emails-and-messages/src/components/messaging-providers-box.tsx b/apps/emails-and-messages/src/components/messaging-providers-box.tsx
index 5a3da8a..eb3d7cf 100644
--- a/apps/emails-and-messages/src/components/messaging-providers-box.tsx
+++ b/apps/emails-and-messages/src/components/messaging-providers-box.tsx
@@ -3,7 +3,11 @@ import { BoxWithBorder } from "./box-with-border";
import { BoxFooter } from "./box-footer";
import { defaultPadding } from "./ui-defaults";
import { useRouter } from "next/router";
-import { SendgridConfiguration } from "../modules/sendgrid/configuration/sendgrid-config";
+import { SendgridConfiguration } from "../modules/sendgrid/configuration/sendgrid-config-schema";
+import { TextLink } from "./text-link";
+import { ChipText } from "./chip-text";
+import Image from "next/image";
+import sendgrid from "../public/sendgrid.png";
const NoExistingConfigurations = () => {
const { replace } = useRouter();
@@ -53,17 +57,32 @@ export const MessagingProvidersBox = ({
return (
-
- Provider
- Name
- Status
+
+
+ Provider
+
+
+ Configuration name
+
+
+ Status
+
{configurations.map((configuration) => (
<>
- Sendgrid
- {configuration.configurationName}
- {configuration.active}
- redirectToEditConfiguration(configuration.id)}>Edit
+
+
+ Sendgrid
+
+
+ {configuration.name}
+
+
+ Edit
+
>
))}
diff --git a/apps/emails-and-messages/src/components/temp.tsx b/apps/emails-and-messages/src/components/temp.tsx
deleted file mode 100644
index 5d6454c..0000000
--- a/apps/emails-and-messages/src/components/temp.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-const x = () => (
-
-
- Provide unique name for your configuration - you can create more than one. For example
- - production and development.
-
- Then, pass your API Key. Obtain it here.
- >
- }
- >
-
-
-
- Remove provider
-
- You can remove provider configuration.
-
- This operation will remove all settings related to this configuration. Data will be
- permanently removed from the App.{" "}
-
-
- This operation cant be undone. You still can create new configuration.
-
-
-
-
-
-
-
-
-
- Provide unique name for your configuration - you can create more than one. For example
- - production and development.
-
- Then, pass your API Key. Obtain it here.
- >
- }
- >
-
-
-
-
-
-
- Enabled
-
-
-
-
-
-
- Enabled
-
-
-
-
-
-
- Enabled
-
-
-
-
-
-
-
-
-
-
- Provide unique name for your configuration - you can create more than one. For example
- - production and development.
-
- Then, pass your API Key. Obtain it here.
- >
- }
- >
-
-
-
-
-
-
-
-
-
-
-
- Manage providers configuration to connect Saleor events with 3rd party services.
-
- }
- >
-
- No providers configured yet
-
-
-
-
-
- console.log("clicked sendgrid")}
- />
-
- console.log("clicked mjml")}
- />
-
-
-)
diff --git a/apps/emails-and-messages/src/components/text-link.tsx b/apps/emails-and-messages/src/components/text-link.tsx
new file mode 100644
index 0000000..f435ad7
--- /dev/null
+++ b/apps/emails-and-messages/src/components/text-link.tsx
@@ -0,0 +1,14 @@
+import { TextProps, Text } from "@saleor/macaw-ui/next";
+import Link from "next/link";
+
+interface TextLinkProps extends TextProps {
+ href: string;
+}
+
+export const TextLink = (props: TextLinkProps) => {
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/apps/emails-and-messages/src/lib/is-available-in-channel.ts b/apps/emails-and-messages/src/lib/is-available-in-channel.ts
index f26a38b..f9fcd70 100644
--- a/apps/emails-and-messages/src/lib/is-available-in-channel.ts
+++ b/apps/emails-and-messages/src/lib/is-available-in-channel.ts
@@ -1,7 +1,8 @@
+import { SendgridConfigurationChannels } from "../modules/sendgrid/configuration/sendgrid-config-schema";
+
interface IsAvailableInChannelArgs {
channel: string;
- restrictedToChannels: string[];
- excludedChannels: string[];
+ channelConfiguration: SendgridConfigurationChannels;
}
/**
@@ -14,14 +15,13 @@ interface IsAvailableInChannelArgs {
*/
export const isAvailableInChannel = ({
channel,
- restrictedToChannels,
- excludedChannels,
+ channelConfiguration,
}: IsAvailableInChannelArgs): boolean => {
- if (channel in excludedChannels) {
- return false;
+ if (!channelConfiguration.override) {
+ return true;
}
- if (restrictedToChannels.length > 0 && !(channel in restrictedToChannels)) {
- return false;
+ if (channelConfiguration.mode === "restrict") {
+ return channel in channelConfiguration.channels;
}
- return true;
+ return !(channel in channelConfiguration.channels);
};
diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/get-sendgrid-configuration.service.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/get-sendgrid-configuration.service.ts
index fd7b576..fd75b0c 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/configuration/get-sendgrid-configuration.service.ts
+++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/get-sendgrid-configuration.service.ts
@@ -1,7 +1,7 @@
import { SendgridConfigurator, PrivateMetadataSendgridConfigurator } from "./sendgrid-configurator";
import { Client } from "urql";
import { logger as pinoLogger } from "../../../lib/logger";
-import { SendgridConfig, SendgridConfiguration } from "./sendgrid-config";
+import { SendgridConfig, SendgridConfiguration } from "./sendgrid-config-schema";
import { FilterConfigurationsArgs, SendgridConfigContainer } from "./sendgrid-config-container";
import { createSettingsManager } from "../../../lib/metadata-manager";
diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts
index a649981..95d278f 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts
+++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts
@@ -4,30 +4,24 @@ import { messageEventTypes } from "../../event-handlers/message-event-types";
import {
SendgridConfig as SendgridConfigurationRoot,
SendgridConfiguration,
-} from "./sendgrid-config";
+ sendgridConfigurationEventSchema,
+ sendgridConfigurationSchema,
+} from "./sendgrid-config-schema";
export const getDefaultEventsConfiguration = (): SendgridConfiguration["events"] =>
- messageEventTypes.map((eventType) => ({
- active: true,
- eventType: eventType,
- template: "",
- }));
+ messageEventTypes.map((eventType) => sendgridConfigurationEventSchema.parse({ eventType }));
export const getDefaultEmptyConfiguration = (): SendgridConfiguration => {
- const defaultConfig: SendgridConfiguration = {
- id: "",
- active: true,
- configurationName: "",
- senderName: undefined,
- senderEmail: undefined,
- apiKey: "",
- sandboxMode: false,
- events: getDefaultEventsConfiguration(),
+ const defaultConfig: SendgridConfiguration = sendgridConfigurationSchema.parse({
+ id: "id",
+ name: "name",
+ apiKey: "key",
channels: {
excludedFrom: [],
restrictedTo: [],
},
- };
+ events: getDefaultEventsConfiguration(),
+ });
return defaultConfig;
};
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 615a2af..6465818 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
@@ -1,46 +1,26 @@
import { z } from "zod";
import { messageEventTypes } from "../../event-handlers/message-event-types";
+import {
+ sendgridConfigurationChannelsSchema,
+ sendgridConfigurationEventSchema,
+ sendgridConfigurationSchema,
+} from "./sendgrid-config-schema";
-export const sendgridConfigurationEventObjectSchema = z.object({
- active: z.boolean(),
- eventType: z.enum(messageEventTypes),
- template: z.string(),
-});
-
-export const sendgridConfigurationBaseObjectSchema = z.object({
- active: z.boolean(),
- configurationName: z.string().min(1),
- sandboxMode: z.boolean(),
- apiKey: z.string().min(1),
- senderName: z.string().min(1).optional(),
- senderEmail: z.string().email().min(5).optional(),
- channels: z.object({
- excludedFrom: z.array(z.string()),
- restrictedTo: z.array(z.string()),
- }),
-});
-
-export const sendgridCreateConfigurationSchema = sendgridConfigurationBaseObjectSchema.pick({
- configurationName: true,
+export const sendgridCreateConfigurationInputSchema = sendgridConfigurationSchema.pick({
+ name: true,
apiKey: true,
});
-export type SendgridCreateConfigurationSchemaType = z.infer<
- typeof sendgridCreateConfigurationSchema
+export type SendgridCreateConfigurationInput = z.infer<
+ typeof sendgridCreateConfigurationInputSchema
>;
-export const sendgridUpdateOrCreateConfigurationSchema =
- sendgridConfigurationBaseObjectSchema.merge(
- z.object({
- id: z.string().optional(),
- })
- );
-export const sendgridGetConfigurationInputSchema = z.object({
- id: z.string(),
-});
-export const sendgridDeleteConfigurationInputSchema = z.object({
- id: z.string(),
+export const sendgridConfigurationIdInputSchema = sendgridConfigurationSchema.pick({
+ id: true,
});
+
+export type SendgridGetConfigurationIdInput = z.infer;
+
export const sendgridGetConfigurationsInputSchema = z
.object({
ids: z.array(z.string()).optional(),
@@ -48,20 +28,61 @@ export const sendgridGetConfigurationsInputSchema = z
})
.optional();
+export type SendgridGetConfigurationsInput = z.infer;
+
export const sendgridUpdateEventConfigurationInputSchema = z
.object({
configurationId: z.string(),
})
- .merge(sendgridConfigurationEventObjectSchema);
+ .merge(sendgridConfigurationEventSchema);
+
+export type SendgridUpdateEventConfigurationInput = z.infer<
+ typeof sendgridUpdateEventConfigurationInputSchema
+>;
export const sendgridGetEventConfigurationInputSchema = z.object({
configurationId: z.string(),
eventType: z.enum(messageEventTypes),
});
-export const sendgridUpdateBasicInformationSchema = sendgridConfigurationBaseObjectSchema
- .pick({
- configurationName: true,
- active: true,
+export type SendgridGetEventConfigurationInput = z.infer<
+ typeof sendgridGetEventConfigurationInputSchema
+>;
+
+export const sendgridUpdateBasicInformationSchema = sendgridConfigurationSchema.pick({
+ id: true,
+ name: true,
+ active: true,
+});
+
+export type SendgridUpdateBasicInformation = z.infer;
+
+export const sendgridUpdateApiConnectionSchema = sendgridConfigurationSchema.pick({
+ id: true,
+ apiKey: true,
+ sandboxMode: true,
+});
+
+export type SendgridUpdateApiConnection = z.infer;
+
+export const sendgridUpdateSenderSchema = sendgridConfigurationSchema.pick({
+ id: true,
+ sender: true,
+});
+export type SendgridUpdateSender = z.infer;
+
+export const sendgridUpdateChannelsSchema = sendgridConfigurationChannelsSchema.merge(
+ sendgridConfigurationSchema.pick({
+ id: true,
})
- .merge(z.object({ id: z.string() }));
+);
+
+export type SendgridUpdateChannels = z.infer;
+
+export const sendgridUpdateEventSchema = sendgridConfigurationEventSchema.merge(
+ sendgridConfigurationSchema.pick({
+ id: true,
+ })
+);
+
+export type SendgridUpdateEvent = z.infer;
diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-schema.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-schema.ts
new file mode 100644
index 0000000..380bd6d
--- /dev/null
+++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-schema.ts
@@ -0,0 +1,37 @@
+import { z } from "zod";
+import { messageEventTypes } from "../../event-handlers/message-event-types";
+
+export const sendgridConfigurationEventSchema = z.object({
+ active: z.boolean().default(false),
+ eventType: z.enum(messageEventTypes),
+ template: z.string().optional(),
+});
+
+export type SendgridEventConfiguration = z.infer;
+
+export const sendgridConfigurationChannelsSchema = z.object({
+ override: z.boolean().default(false),
+ channels: z.array(z.string()).default([]),
+ mode: z.enum(["exclude", "restrict"]).default("restrict"),
+});
+
+export type SendgridConfigurationChannels = z.infer;
+
+export const sendgridConfigurationSchema = z.object({
+ id: z.string().min(1),
+ active: z.boolean().default(true),
+ name: z.string().min(1),
+ sandboxMode: z.boolean().default(false),
+ apiKey: z.string().min(1),
+ sender: z.string().min(1).optional(),
+ channels: sendgridConfigurationChannelsSchema,
+ events: z.array(sendgridConfigurationEventSchema),
+});
+
+export type SendgridConfiguration = z.infer;
+
+export const sendgridConfigSchema = z.object({
+ configurations: z.array(sendgridConfigurationSchema),
+});
+
+export type SendgridConfig = z.infer;
diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts
deleted file mode 100644
index d231e30..0000000
--- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { MessageEventTypes } from "../../event-handlers/message-event-types";
-
-export interface SendgridEventConfiguration {
- active: boolean;
- eventType: MessageEventTypes;
- template: string;
-}
-
-export interface SendgridConfiguration {
- id: string;
- active: boolean;
- configurationName: string;
- sandboxMode: boolean;
- senderName?: string;
- senderEmail?: string;
- apiKey: string;
- events: SendgridEventConfiguration[];
- channels: {
- excludedFrom: string[];
- restrictedTo: string[];
- };
-}
-
-export type SendgridConfig = {
- configurations: SendgridConfiguration[];
-};
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 cd02720..3325af1 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
@@ -1,18 +1,21 @@
import { logger as pinoLogger } from "../../../lib/logger";
import {
- sendgridCreateConfigurationSchema,
- sendgridDeleteConfigurationInputSchema,
- sendgridGetConfigurationInputSchema,
+ sendgridConfigurationIdInputSchema,
+ sendgridCreateConfigurationInputSchema,
sendgridGetConfigurationsInputSchema,
sendgridGetEventConfigurationInputSchema,
+ sendgridUpdateApiConnectionSchema,
sendgridUpdateBasicInformationSchema,
+ sendgridUpdateChannelsSchema,
sendgridUpdateEventConfigurationInputSchema,
- sendgridUpdateOrCreateConfigurationSchema,
+ sendgridUpdateEventSchema,
+ sendgridUpdateSenderSchema,
} from "./sendgrid-config-input-schema";
import { SendgridConfigurationService } from "./get-sendgrid-configuration.service";
import { router } from "../../trpc/trpc-server";
import { protectedClientProcedure } from "../../trpc/protected-client-procedure";
import { TRPCError } from "@trpc/server";
+import { getDefaultEmptyConfiguration } from "./sendgrid-config-container";
// Allow access only for the dashboard users and attaches the
// configuration service to the context
@@ -36,7 +39,7 @@ export const sendgridConfigurationRouter = router({
}),
getConfiguration: protectedWithConfigurationService
.meta({ requiredClientPermissions: ["MANAGE_APPS"] })
- .input(sendgridGetConfigurationInputSchema)
+ .input(sendgridConfigurationIdInputSchema)
.query(async ({ ctx, input }) => {
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
logger.debug(input, "sendgridConfigurationRouter.get called");
@@ -52,15 +55,19 @@ export const sendgridConfigurationRouter = router({
}),
createConfiguration: protectedWithConfigurationService
.meta({ requiredClientPermissions: ["MANAGE_APPS"] })
- .input(sendgridCreateConfigurationSchema)
+ .input(sendgridCreateConfigurationInputSchema)
.mutation(async ({ ctx, input }) => {
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
logger.debug(input, "sendgridConfigurationRouter.create called");
- return await ctx.configurationService.createConfiguration(input);
+ const newConfiguration = {
+ ...getDefaultEmptyConfiguration(),
+ ...input,
+ };
+ return await ctx.configurationService.createConfiguration(newConfiguration);
}),
deleteConfiguration: protectedWithConfigurationService
.meta({ requiredClientPermissions: ["MANAGE_APPS"] })
- .input(sendgridDeleteConfigurationInputSchema)
+ .input(sendgridConfigurationIdInputSchema)
.mutation(async ({ ctx, input }) => {
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
logger.debug(input, "sendgridConfigurationRouter.delete called");
@@ -74,33 +81,33 @@ export const sendgridConfigurationRouter = router({
await ctx.configurationService.deleteConfiguration(input);
return null;
}),
- updateOrCreateConfiguration: protectedWithConfigurationService
- .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
- .input(sendgridUpdateOrCreateConfigurationSchema)
- .mutation(async ({ ctx, input }) => {
- const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
- logger.debug(input, "sendgridConfigurationRouter.update or create called");
+ // updateOrCreateConfiguration: protectedWithConfigurationService
+ // .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ // .input(sendgridUpdateOrCreateConfigurationSchema)
+ // .mutation(async ({ ctx, input }) => {
+ // const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
+ // logger.debug(input, "sendgridConfigurationRouter.update or create called");
- const { id } = input;
- if (!id) {
- return await ctx.configurationService.createConfiguration(input);
- } else {
- const existingConfiguration = await ctx.configurationService.getConfiguration({ id });
- if (!existingConfiguration) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "Configuration not found",
- });
- }
- const configuration = {
- id,
- ...input,
- events: existingConfiguration.events,
- };
- await ctx.configurationService.updateConfiguration(configuration);
- return configuration;
- }
- }),
+ // const { id } = input;
+ // if (!id) {
+ // return await ctx.configurationService.createConfiguration(input);
+ // } else {
+ // const existingConfiguration = await ctx.configurationService.getConfiguration({ id });
+ // if (!existingConfiguration) {
+ // throw new TRPCError({
+ // code: "BAD_REQUEST",
+ // message: "Configuration not found",
+ // });
+ // }
+ // const configuration = {
+ // id,
+ // ...input,
+ // events: existingConfiguration.events,
+ // };
+ // await ctx.configurationService.updateConfiguration(configuration);
+ // return configuration;
+ // }
+ // }),
getEventConfiguration: protectedWithConfigurationService
.meta({ requiredClientPermissions: ["MANAGE_APPS"] })
.input(sendgridGetEventConfigurationInputSchema)
@@ -175,4 +182,92 @@ export const sendgridConfigurationRouter = router({
await ctx.configurationService.updateConfiguration({ ...configuration, ...input });
return configuration;
}),
+ updateApiConnection: protectedWithConfigurationService
+ .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ .input(sendgridUpdateApiConnectionSchema)
+ .mutation(async ({ ctx, input }) => {
+ const configuration = await ctx.configurationService.getConfiguration({
+ id: input.id,
+ });
+
+ if (!configuration) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Configuration not found",
+ });
+ }
+
+ await ctx.configurationService.updateConfiguration({ ...configuration, ...input });
+ return configuration;
+ }),
+
+ updateSender: protectedWithConfigurationService
+ .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ .input(sendgridUpdateSenderSchema)
+ .mutation(async ({ ctx, input }) => {
+ const configuration = await ctx.configurationService.getConfiguration({
+ id: input.id,
+ });
+
+ if (!configuration) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Configuration not found",
+ });
+ }
+ await ctx.configurationService.updateConfiguration({ ...configuration, ...input });
+ return configuration;
+ }),
+ updateChannels: protectedWithConfigurationService
+ .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ .input(sendgridUpdateChannelsSchema)
+ .mutation(async ({ ctx, input }) => {
+ const configuration = await ctx.configurationService.getConfiguration({
+ id: input.id,
+ });
+
+ if (!configuration) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Configuration not found",
+ });
+ }
+ configuration.channels = {
+ override: input.override,
+ channels: input.channels,
+ mode: input.mode,
+ };
+ await ctx.configurationService.updateConfiguration(configuration);
+ return configuration;
+ }),
+
+ updateEvent: protectedWithConfigurationService
+ .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ .input(sendgridUpdateEventSchema)
+ .mutation(async ({ ctx, input }) => {
+ const configuration = await ctx.configurationService.getConfiguration({
+ id: input.id,
+ });
+
+ if (!configuration) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Configuration not found",
+ });
+ }
+ const event = configuration.events.find((e) => e.eventType === input.eventType);
+
+ if (!event) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Configuration event not found",
+ });
+ }
+
+ event.template = input.template;
+ event.active = input.active;
+
+ await ctx.configurationService.updateConfiguration(configuration);
+ return configuration;
+ }),
});
diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configurator.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configurator.ts
index dea0caa..48663f7 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configurator.ts
+++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-configurator.ts
@@ -1,4 +1,4 @@
-import { SendgridConfig } from "./sendgrid-config";
+import { SendgridConfig } from "./sendgrid-config-schema";
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
export interface SendgridConfigurator {
diff --git a/apps/emails-and-messages/src/modules/sendgrid/send-sendgrid.ts b/apps/emails-and-messages/src/modules/sendgrid/send-sendgrid.ts
index c692723..357b6c4 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/send-sendgrid.ts
+++ b/apps/emails-and-messages/src/modules/sendgrid/send-sendgrid.ts
@@ -1,5 +1,5 @@
import { logger as pinoLogger } from "../../lib/logger";
-import { SendgridConfiguration } from "./configuration/sendgrid-config";
+import { SendgridConfiguration } from "./configuration/sendgrid-config-schema";
import { MailService } from "@sendgrid/mail";
import { MessageEventTypes } from "../event-handlers/message-event-types";
diff --git a/apps/emails-and-messages/src/modules/sendgrid/sendgrid-api.ts b/apps/emails-and-messages/src/modules/sendgrid/sendgrid-api.ts
index 5f2d821..7ebaad4 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/sendgrid-api.ts
+++ b/apps/emails-and-messages/src/modules/sendgrid/sendgrid-api.ts
@@ -30,7 +30,7 @@ export const fetchTemplates =
};
const templates =
resJson.result?.map((r) => ({
- value: r.id,
+ value: r.id.toString(),
label: r.name,
})) || [];
return templates;
@@ -65,7 +65,7 @@ export const fetchSenders =
};
const senders =
resJson.results?.map((r) => ({
- value: r.id,
+ value: r.id.toString(),
label: `${r.nickname} (${r.from_email})`,
nickname: r.nickname,
from_email: r.from_email,
diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/api-connection-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/api-connection-section.tsx
index e9f4513..04108d1 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/ui/api-connection-section.tsx
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/api-connection-section.tsx
@@ -1,76 +1,64 @@
-import { useRouter } from "next/router";
-import { SendgridConfiguration } from "../configuration/sendgrid-config";
+import { SendgridConfiguration } from "../configuration/sendgrid-config-schema";
import { BoxWithBorder } from "../../../components/box-with-border";
-import { Box, Button, Input, RadioGroup, Text } from "@saleor/macaw-ui/next";
+import { Box, Button, Input, Text } from "@saleor/macaw-ui/next";
import { defaultPadding } from "../../../components/ui-defaults";
import { useDashboardNotification } from "@saleor/apps-shared";
import { trpcClient } from "../../trpc/trpc-client";
-import { sendgridUpdateBasicInformationSchema } from "../configuration/sendgrid-config-input-schema";
-import { z } from "zod";
+import { SendgridUpdateApiConnection } from "../configuration/sendgrid-config-input-schema";
import { Controller, useForm } from "react-hook-form";
import { BoxFooter } from "../../../components/box-footer";
import { SectionWithDescription } from "../../../components/section-with-description";
interface ApiConnectionSectionProps {
- configuration?: SendgridConfiguration;
+ configuration: SendgridConfiguration;
}
export const ApiConnectionSection = ({ configuration }: ApiConnectionSectionProps) => {
- const { replace } = useRouter();
const { notifySuccess, notifyError } = useDashboardNotification();
- const { handleSubmit, control, setError } = useForm<
- z.infer
- >({
+ const { handleSubmit, control, setError, register } = useForm({
defaultValues: {
- configurationName: configuration?.configurationName,
- active: configuration?.active,
+ id: configuration.id,
+ apiKey: configuration.apiKey,
+ sandboxMode: configuration.sandboxMode,
},
});
- const { mutate: createConfiguration } =
- trpcClient.sendgridConfiguration.updateBasicInformation.useMutation({
- onSuccess: async (data, variables) => {
- notifySuccess("Configuration saved");
- // TODO: redirect to configuration details based on id
- },
- onError(error) {
- let isFieldErrorSet = false;
- const fieldErrors = error.data?.zodError?.fieldErrors || {};
- for (const fieldName in fieldErrors) {
- for (const message of fieldErrors[fieldName] || []) {
- isFieldErrorSet = true;
- setError(fieldName as keyof z.infer, {
- type: "manual",
- message,
- });
- }
+ const trpcContext = trpcClient.useContext();
+ const { mutate } = trpcClient.sendgridConfiguration.updateApiConnection.useMutation({
+ onSuccess: async (data, variables) => {
+ notifySuccess("Configuration saved");
+ trpcContext.sendgridConfiguration.invalidate();
+ },
+ onError(error) {
+ let isFieldErrorSet = false;
+ const fieldErrors = error.data?.zodError?.fieldErrors || {};
+ for (const fieldName in fieldErrors) {
+ for (const message of fieldErrors[fieldName] || []) {
+ isFieldErrorSet = true;
+ setError(fieldName as keyof SendgridUpdateApiConnection, {
+ type: "manual",
+ message,
+ });
}
- const formErrors = error.data?.zodError?.formErrors || [];
- const formErrorMessage = formErrors.length ? formErrors.join("\n") : undefined;
+ }
+ const formErrors = error.data?.zodError?.formErrors || [];
+ const formErrorMessage = formErrors.length ? formErrors.join("\n") : undefined;
- notifyError(
- "Could not save the configuration",
- isFieldErrorSet ? "Submitted form contain errors" : "Error saving configuration",
- formErrorMessage
- );
- },
- });
-
- if (!configuration) {
- return (
-
- Loading
-
- );
- }
+ notifyError(
+ "Could not save the configuration",
+ isFieldErrorSet ? "Submitted form contain errors" : "Error saving configuration",
+ formErrorMessage
+ );
+ },
+ });
return (
diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/basic-information-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/basic-information-section.tsx
index 81283da..743f6c0 100644
--- a/apps/emails-and-messages/src/modules/sendgrid/ui/basic-information-section.tsx
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/basic-information-section.tsx
@@ -1,69 +1,60 @@
-import { useRouter } from "next/router";
-import { SendgridConfiguration } from "../configuration/sendgrid-config";
+import { SendgridConfiguration } from "../configuration/sendgrid-config-schema";
import { BoxWithBorder } from "../../../components/box-with-border";
import { Box, Button, Input, RadioGroup, Text } from "@saleor/macaw-ui/next";
import { defaultPadding } from "../../../components/ui-defaults";
import { useDashboardNotification } from "@saleor/apps-shared";
import { trpcClient } from "../../trpc/trpc-client";
-import { sendgridUpdateBasicInformationSchema } from "../configuration/sendgrid-config-input-schema";
+import {
+ SendgridUpdateBasicInformation,
+ sendgridUpdateBasicInformationSchema,
+} from "../configuration/sendgrid-config-input-schema";
import { z } from "zod";
import { Controller, useForm } from "react-hook-form";
import { BoxFooter } from "../../../components/box-footer";
import { SectionWithDescription } from "../../../components/section-with-description";
interface BasicInformationSectionProps {
- configuration?: SendgridConfiguration;
+ configuration: SendgridConfiguration;
}
export const BasicInformationSection = ({ configuration }: BasicInformationSectionProps) => {
- const { replace } = useRouter();
const { notifySuccess, notifyError } = useDashboardNotification();
-
- const { handleSubmit, control, setError } = useForm<
- z.infer
- >({
+ const { handleSubmit, control, setError, register } = useForm({
defaultValues: {
- configurationName: configuration?.configurationName,
- active: configuration?.active,
+ id: configuration.id,
+ name: configuration.name,
+ active: configuration.active,
},
});
- const { mutate: createConfiguration } =
- trpcClient.sendgridConfiguration.updateBasicInformation.useMutation({
- onSuccess: async (data, variables) => {
- notifySuccess("Configuration saved");
- // TODO: redirect to configuration details based on id
- },
- onError(error) {
- let isFieldErrorSet = false;
- const fieldErrors = error.data?.zodError?.fieldErrors || {};
- for (const fieldName in fieldErrors) {
- for (const message of fieldErrors[fieldName] || []) {
- isFieldErrorSet = true;
- setError(fieldName as keyof z.infer, {
- type: "manual",
- message,
- });
- }
+ const trpcContext = trpcClient.useContext();
+ const { mutate } = trpcClient.sendgridConfiguration.updateBasicInformation.useMutation({
+ onSuccess: async (data, variables) => {
+ notifySuccess("Configuration saved");
+ trpcContext.sendgridConfiguration.invalidate();
+ },
+ onError(error) {
+ let isFieldErrorSet = false;
+ const fieldErrors = error.data?.zodError?.fieldErrors || {};
+ for (const fieldName in fieldErrors) {
+ for (const message of fieldErrors[fieldName] || []) {
+ isFieldErrorSet = true;
+ setError(fieldName as keyof z.infer, {
+ type: "manual",
+ message,
+ });
}
- const formErrors = error.data?.zodError?.formErrors || [];
- const formErrorMessage = formErrors.length ? formErrors.join("\n") : undefined;
+ }
+ const formErrors = error.data?.zodError?.formErrors || [];
+ const formErrorMessage = formErrors.length ? formErrors.join("\n") : undefined;
- notifyError(
- "Could not save the configuration",
- isFieldErrorSet ? "Submitted form contain errors" : "Error saving configuration",
- formErrorMessage
- );
- },
- });
-
- if (!configuration) {
- return (
-
- Loading
-
- );
- }
+ notifyError(
+ "Could not save the configuration",
+ isFieldErrorSet ? "Submitted form contain errors" : "Error saving configuration",
+ formErrorMessage
+ );
+ },
+ });
return (