diff --git a/apps/emails-and-messages/package.json b/apps/emails-and-messages/package.json
index a672ecc..aacf571 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.72",
+ "@saleor/macaw-ui": "0.8.0-pre.76",
"@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/box-footer.tsx b/apps/emails-and-messages/src/components/box-footer.tsx
new file mode 100644
index 0000000..f588b37
--- /dev/null
+++ b/apps/emails-and-messages/src/components/box-footer.tsx
@@ -0,0 +1,19 @@
+import { Box, BoxProps } from "@saleor/macaw-ui/next";
+import { defaultPadding } from "./ui-defaults";
+
+export const BoxFooter = (props: BoxProps) => {
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/box-with-border.tsx b/apps/emails-and-messages/src/components/box-with-border.tsx
new file mode 100644
index 0000000..c2ea45a
--- /dev/null
+++ b/apps/emails-and-messages/src/components/box-with-border.tsx
@@ -0,0 +1,15 @@
+import { Box, BoxProps } from "@saleor/macaw-ui/next";
+
+export const BoxWithBorder = (props: BoxProps) => {
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/breadcrumbs.tsx b/apps/emails-and-messages/src/components/breadcrumbs.tsx
new file mode 100644
index 0000000..661e761
--- /dev/null
+++ b/apps/emails-and-messages/src/components/breadcrumbs.tsx
@@ -0,0 +1,33 @@
+import { Box, Text } from "@saleor/macaw-ui/next";
+
+interface BreadcrumbsProps {
+ items: Array<{ name: string; href?: string }>;
+}
+
+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
+
+ return (
+
+ {i.map((item) => (
+ <>
+
+ {item.name}
+
+
+ {">"}
+
+ >
+ ))}
+
+ {lastItem.name}
+
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/loading-indicator.tsx b/apps/emails-and-messages/src/components/loading-indicator.tsx
new file mode 100644
index 0000000..9d999e4
--- /dev/null
+++ b/apps/emails-and-messages/src/components/loading-indicator.tsx
@@ -0,0 +1,3 @@
+export const LoadingIndicator = () => {
+ return <>LOADING>;
+};
diff --git a/apps/emails-and-messages/src/components/messaging-providers-box.tsx b/apps/emails-and-messages/src/components/messaging-providers-box.tsx
new file mode 100644
index 0000000..5a3da8a
--- /dev/null
+++ b/apps/emails-and-messages/src/components/messaging-providers-box.tsx
@@ -0,0 +1,75 @@
+import { Box, Button, Text } from "@saleor/macaw-ui/next";
+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";
+
+const NoExistingConfigurations = () => {
+ const { replace } = useRouter();
+
+ const redirectToProvidersSelection = () => {
+ replace("/configuration/choose-provider");
+ };
+
+ return (
+
+ No providers configured yet
+
+
+ );
+};
+
+interface MessagingProvidersSectionProps {
+ configurations: SendgridConfiguration[];
+ isLoading: boolean;
+}
+
+export const MessagingProvidersBox = ({
+ configurations,
+ isLoading: loading,
+}: MessagingProvidersSectionProps) => {
+ const { replace } = useRouter();
+
+ if (loading) {
+ return (
+
+ Loading
+
+ );
+ }
+
+ if (configurations.length === 0) {
+ return ;
+ }
+
+ const redirectToProvidersSelection = () => {
+ replace("/configuration/choose-provider");
+ };
+
+ const redirectToEditConfiguration = (configurationId: string) => {
+ replace(`/configuration/sendgrid/edit/${configurationId}`);
+ };
+
+ return (
+
+
+ Provider
+ Name
+ Status
+
+ {configurations.map((configuration) => (
+ <>
+ Sendgrid
+ {configuration.configurationName}
+ {configuration.active}
+ redirectToEditConfiguration(configuration.id)}>Edit
+ >
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/section-with-description.tsx b/apps/emails-and-messages/src/components/section-with-description.tsx
new file mode 100644
index 0000000..463f629
--- /dev/null
+++ b/apps/emails-and-messages/src/components/section-with-description.tsx
@@ -0,0 +1,27 @@
+import { Box, Text } from "@saleor/macaw-ui/next";
+
+interface SectionWithDescriptionProps {
+ title: string;
+ description?: React.ReactNode;
+ children?: React.ReactNode;
+}
+export const SectionWithDescription = (props: SectionWithDescriptionProps) => {
+ return (
+
+
+
+ {props.title}
+
+ {props.description}
+
+ {!!props.children && (
+
+ {props.children}
+
+ )}
+
+ );
+};
diff --git a/apps/emails-and-messages/src/components/temp.tsx b/apps/emails-and-messages/src/components/temp.tsx
new file mode 100644
index 0000000..5d6454c
--- /dev/null
+++ b/apps/emails-and-messages/src/components/temp.tsx
@@ -0,0 +1,160 @@
+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/ui-defaults.ts b/apps/emails-and-messages/src/components/ui-defaults.ts
new file mode 100644
index 0000000..cb54a9c
--- /dev/null
+++ b/apps/emails-and-messages/src/components/ui-defaults.ts
@@ -0,0 +1 @@
+export const defaultPadding = 6;
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 b6dd299..615a2af 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
@@ -20,10 +20,15 @@ export const sendgridConfigurationBaseObjectSchema = z.object({
}),
});
-export const sendgridCreateConfigurationSchema = sendgridConfigurationBaseObjectSchema.omit({
- senderEmail: true,
- senderName: true,
+export const sendgridCreateConfigurationSchema = sendgridConfigurationBaseObjectSchema.pick({
+ configurationName: true,
+ apiKey: true,
});
+
+export type SendgridCreateConfigurationSchemaType = z.infer<
+ typeof sendgridCreateConfigurationSchema
+>;
+
export const sendgridUpdateOrCreateConfigurationSchema =
sendgridConfigurationBaseObjectSchema.merge(
z.object({
@@ -53,3 +58,10 @@ export const sendgridGetEventConfigurationInputSchema = z.object({
configurationId: z.string(),
eventType: z.enum(messageEventTypes),
});
+
+export const sendgridUpdateBasicInformationSchema = sendgridConfigurationBaseObjectSchema
+ .pick({
+ configurationName: true,
+ active: true,
+ })
+ .merge(z.object({ id: z.string() }));
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 99253e7..cd02720 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
@@ -5,6 +5,7 @@ import {
sendgridGetConfigurationInputSchema,
sendgridGetConfigurationsInputSchema,
sendgridGetEventConfigurationInputSchema,
+ sendgridUpdateBasicInformationSchema,
sendgridUpdateEventConfigurationInputSchema,
sendgridUpdateOrCreateConfigurationSchema,
} from "./sendgrid-config-input-schema";
@@ -156,4 +157,22 @@ export const sendgridConfigurationRouter = router({
await ctx.configurationService.updateConfiguration(configuration);
return configuration;
}),
+ updateBasicInformation: protectedWithConfigurationService
+ .meta({ requiredClientPermissions: ["MANAGE_APPS"] })
+ .input(sendgridUpdateBasicInformationSchema)
+ .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;
+ }),
});
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
new file mode 100644
index 0000000..e9f4513
--- /dev/null
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/api-connection-section.tsx
@@ -0,0 +1,126 @@
+import { useRouter } from "next/router";
+import { SendgridConfiguration } from "../configuration/sendgrid-config";
+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 { z } from "zod";
+import { Controller, useForm } from "react-hook-form";
+import { BoxFooter } from "../../../components/box-footer";
+import { SectionWithDescription } from "../../../components/section-with-description";
+
+interface ApiConnectionSectionProps {
+ configuration?: SendgridConfiguration;
+}
+
+export const ApiConnectionSection = ({ configuration }: ApiConnectionSectionProps) => {
+ const { replace } = useRouter();
+ const { notifySuccess, notifyError } = useDashboardNotification();
+
+ const { handleSubmit, control, setError } = useForm<
+ z.infer
+ >({
+ defaultValues: {
+ configurationName: configuration?.configurationName,
+ 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 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
+
+ );
+ }
+
+ 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
new file mode 100644
index 0000000..81283da
--- /dev/null
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/basic-information-section.tsx
@@ -0,0 +1,134 @@
+import { useRouter } from "next/router";
+import { SendgridConfiguration } from "../configuration/sendgrid-config";
+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 { 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;
+}
+
+export const BasicInformationSection = ({ configuration }: BasicInformationSectionProps) => {
+ const { replace } = useRouter();
+ const { notifySuccess, notifyError } = useDashboardNotification();
+
+ const { handleSubmit, control, setError } = useForm<
+ z.infer
+ >({
+ defaultValues: {
+ configurationName: configuration?.configurationName,
+ 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 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
+
+ );
+ }
+
+ return (
+
+ 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.
+
+ }
+ >
+
+
+
+
+ );
+};
diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/dangrous-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/dangrous-section.tsx
new file mode 100644
index 0000000..393c178
--- /dev/null
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/dangrous-section.tsx
@@ -0,0 +1,44 @@
+import { Box, Button, Text } from "@saleor/macaw-ui/next";
+import { BoxWithBorder } from "../../../components/box-with-border";
+import { SectionWithDescription } from "../../../components/section-with-description";
+import { defaultPadding } from "../../../components/ui-defaults";
+import { BoxFooter } from "../../../components/box-footer";
+import { SendgridConfiguration } from "../configuration/sendgrid-config";
+
+interface DangerousSectionProps {
+ configuration?: SendgridConfiguration;
+}
+
+export const DangerousSection = ({ configuration }: DangerousSectionProps) => {
+ const onRemoveConfiguration = () => {
+ console.log("remove", configuration?.id);
+ };
+
+ return (
+
+
+
+
+ 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.
+
+
+
+
+
+
+ );
+};
diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/sender-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/sender-section.tsx
new file mode 100644
index 0000000..de89bc6
--- /dev/null
+++ b/apps/emails-and-messages/src/modules/sendgrid/ui/sender-section.tsx
@@ -0,0 +1,105 @@
+import { useRouter } from "next/router";
+import { SendgridConfiguration } from "../configuration/sendgrid-config";
+import { BoxWithBorder } from "../../../components/box-with-border";
+import { Box, Button, Combobox, 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 { Controller, useForm } from "react-hook-form";
+import { BoxFooter } from "../../../components/box-footer";
+import { SectionWithDescription } from "../../../components/section-with-description";
+
+interface SenderSectionProps {
+ configuration?: SendgridConfiguration;
+}
+
+export const SenderSection = ({ configuration }: SenderSectionProps) => {
+ const { replace } = useRouter();
+ const { notifySuccess, notifyError } = useDashboardNotification();
+
+ const { handleSubmit, control, setError } = useForm<
+ z.infer
+ >({
+ defaultValues: {
+ configurationName: configuration?.configurationName,
+ 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 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
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/apps/emails-and-messages/src/pages/_app.tsx b/apps/emails-and-messages/src/pages/_app.tsx
index 125083e..ea838bd 100644
--- a/apps/emails-and-messages/src/pages/_app.tsx
+++ b/apps/emails-and-messages/src/pages/_app.tsx
@@ -1,65 +1,15 @@
+import "@saleor/macaw-ui/next/style";
import "../styles/globals.css";
-import { Theme } from "@material-ui/core/styles";
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
-import {
- dark,
- light,
- SaleorThemeColors,
- ThemeProvider as MacawUIThemeProvider,
-} from "@saleor/macaw-ui";
-import React, { PropsWithChildren, useEffect } from "react";
+import React from "react";
import { AppProps } from "next/app";
+import { ThemeProvider } from "@saleor/macaw-ui/next";
import { ThemeSynchronizer } from "../lib/theme-synchronizer";
import { NoSSRWrapper } from "../lib/no-ssr-wrapper";
import { trpcClient } from "../modules/trpc/trpc-client";
-import { createGenerateClassName, StylesProvider } from "@material-ui/core";
-
-type PalettesOverride = Record<"light" | "dark", SaleorThemeColors>;
-
-/**
- * Temporary override of colors, to match new dashboard palette.
- * Long term this will be replaced with Macaw UI 2.x with up to date design tokens
- */
-const palettes: PalettesOverride = {
- light: {
- ...light,
- background: {
- default: "#fff",
- paper: "#fff",
- },
- },
- dark: {
- ...dark,
- background: {
- default: "hsla(211, 42%, 14%, 1)",
- paper: "hsla(211, 42%, 14%, 1)",
- },
- },
-};
-
-const themeOverrides: Partial = {
- overrides: {
- MuiTableCell: {
- body: {
- paddingBottom: 8,
- paddingTop: 8,
- },
- root: {
- height: 56,
- paddingBottom: 4,
- paddingTop: 4,
- },
- },
- },
-};
-
-const generateClassName = createGenerateClassName({
- productionPrefix: "c",
- disableGlobal: true,
-});
/**
* Ensure instance is a singleton.
@@ -67,34 +17,15 @@ const generateClassName = createGenerateClassName({
*/
export const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined;
-/**
- * That's a hack required by Macaw-UI incompatibility with React@18
- */
-const ThemeProvider = MacawUIThemeProvider as React.FC<
- PropsWithChildren<{ overrides?: Partial; ssr: boolean; palettes: PalettesOverride }>
->;
-
function NextApp({ Component, pageProps }: AppProps) {
- /**
- * Configure JSS (used by MacawUI) for SSR. If Macaw is not used, can be removed.
- */
- useEffect(() => {
- const jssStyles = document.querySelector("#jss-server-side");
- if (jssStyles) {
- jssStyles?.parentElement?.removeChild(jssStyles);
- }
- }, []);
-
return (
-
-
-
-
-
-
-
+
+
+
+
+
);
diff --git a/apps/emails-and-messages/src/pages/api/register.ts b/apps/emails-and-messages/src/pages/api/register.ts
index 03e1e55..45d2996 100644
--- a/apps/emails-and-messages/src/pages/api/register.ts
+++ b/apps/emails-and-messages/src/pages/api/register.ts
@@ -27,16 +27,16 @@ export default createAppRegisterHandler({
],
onAuthAplSaved: async (request, ctx) => {
// Subscribe to Notify using the mutation since it does not use subscriptions and can't be subscribed via manifest
- logger.debug("onAuthAplSaved executing");
-
- const baseUrl = getBaseUrl(request.headers);
- const client = createClient(ctx.authData.saleorApiUrl, async () =>
- Promise.resolve({ token: ctx.authData.token })
- );
- await registerNotifyWebhook({
- client: client,
- baseUrl: baseUrl,
- });
- logger.debug("Webhook registered");
+ // FIXME:
+ // logger.debug("onAuthAplSaved executing");
+ // const baseUrl = getBaseUrl(request.headers);
+ // const client = createClient(ctx.authData.saleorApiUrl, async () =>
+ // Promise.resolve({ token: ctx.authData.token })
+ // );
+ // await registerNotifyWebhook({
+ // client: client,
+ // baseUrl: baseUrl,
+ // });
+ // logger.debug("Webhook registered");
},
});
diff --git a/apps/emails-and-messages/src/pages/configuration/choose-provider.tsx b/apps/emails-and-messages/src/pages/configuration/choose-provider.tsx
new file mode 100644
index 0000000..672c8b6
--- /dev/null
+++ b/apps/emails-and-messages/src/pages/configuration/choose-provider.tsx
@@ -0,0 +1,38 @@
+import { Box, Text } from "@saleor/macaw-ui/next";
+import { NextPage } from "next";
+import { Breadcrumbs } from "../../components/breadcrumbs";
+import { SectionWithDescription } from "../../components/section-with-description";
+import { ProviderSelectionBox } from "../../modules/app-configuration/ui/provider-selection-box";
+import { useRouter } from "next/router";
+
+const ChooseProviderPage: NextPage = () => {
+ const { replace } = useRouter();
+
+ return (
+
+
+
+
+ Select and configure providers to connect Saleor with selected services.
+
+
+
+
+ replace("/configuration/sendgrid/new")}
+ />
+
+ replace("/configuration/mjml/new")}
+ />
+
+
+
+ );
+};
+
+export default ChooseProviderPage;
diff --git a/apps/emails-and-messages/src/pages/configuration/index.tsx b/apps/emails-and-messages/src/pages/configuration/index.tsx
new file mode 100644
index 0000000..3c23ec4
--- /dev/null
+++ b/apps/emails-and-messages/src/pages/configuration/index.tsx
@@ -0,0 +1,36 @@
+import { Box, Text } from "@saleor/macaw-ui/next";
+import { NextPage } from "next";
+import { Breadcrumbs } from "../../components/breadcrumbs";
+import { SectionWithDescription } from "../../components/section-with-description";
+import { MessagingProvidersBox } from "../../components/messaging-providers-box";
+import { trpcClient } from "../../modules/trpc/trpc-client";
+
+const ConfigurationPage: NextPage = () => {
+ const { data, isLoading } = trpcClient.sendgridConfiguration.getConfigurations.useQuery();
+
+ return (
+
+
+
+
+
+ Configure Emails & Messages app to deliver Saleor Events webhooks to various messaging
+ providers
+
+
+
+
+ Manage providers configuration to connect Saleor events with 3rd party services.
+
+ }
+ >
+
+
+
+ );
+};
+
+export default ConfigurationPage;
diff --git a/apps/emails-and-messages/src/pages/configuration/sendgrid/edit/[id].tsx b/apps/emails-and-messages/src/pages/configuration/sendgrid/edit/[id].tsx
new file mode 100644
index 0000000..e398310
--- /dev/null
+++ b/apps/emails-and-messages/src/pages/configuration/sendgrid/edit/[id].tsx
@@ -0,0 +1,38 @@
+import { Box, Text } from "@saleor/macaw-ui/next";
+import { NextPage } from "next";
+import { Breadcrumbs } from "../../../../components/breadcrumbs";
+import { trpcClient } from "../../../../modules/trpc/trpc-client";
+import { useRouter } from "next/router";
+import { BasicInformationSection } from "../../../../modules/sendgrid/ui/basic-information-section";
+import { DangerousSection } from "../../../../modules/sendgrid/ui/dangrous-section";
+import { SectionWithDescription } from "../../../../components/section-with-description";
+import { ApiConnectionSection } from "../../../../modules/sendgrid/ui/api-connection-section";
+import { SenderSection } from "../../../../modules/sendgrid/ui/sender-section";
+
+const EditSendgridConfigurationPage: NextPage = () => {
+ const router = useRouter();
+ const { id } = router.query;
+
+ const { data: configuration } = trpcClient.sendgridConfiguration.getConfiguration.useQuery({
+ id: id as string,
+ });
+
+ return (
+
+
+
+
+ Connect Sendgrid with Saleor.
+
+
+ {!!configuration && }
+ {!!configuration && }
+ {!!configuration && }
+ {!!configuration && }
+
+ );
+};
+
+export default EditSendgridConfigurationPage;
diff --git a/apps/emails-and-messages/src/pages/configuration/sendgrid/new.tsx b/apps/emails-and-messages/src/pages/configuration/sendgrid/new.tsx
new file mode 100644
index 0000000..06caccd
--- /dev/null
+++ b/apps/emails-and-messages/src/pages/configuration/sendgrid/new.tsx
@@ -0,0 +1,130 @@
+import { Box, Button, Input, Text } from "@saleor/macaw-ui/next";
+import { NextPage } from "next";
+import { Breadcrumbs } from "../../../components/breadcrumbs";
+import { SectionWithDescription } from "../../../components/section-with-description";
+import { BoxWithBorder } from "../../../components/box-with-border";
+import { defaultPadding } from "../../../components/ui-defaults";
+import { BoxFooter } from "../../../components/box-footer";
+import { trpcClient } from "../../../modules/trpc/trpc-client";
+import { useDashboardNotification } from "@saleor/apps-shared/src/use-dashboard-notification";
+import { Controller, useForm } from "react-hook-form";
+import { SendgridCreateConfigurationSchemaType } from "../../../modules/sendgrid/configuration/sendgrid-config-input-schema";
+
+const NewSendgridConfigurationPage: NextPage = () => {
+ const { notifySuccess, notifyError } = useDashboardNotification();
+
+ const { handleSubmit, control, setError } = useForm();
+
+ const { mutate: createConfiguration } =
+ trpcClient.sendgridConfiguration.createConfiguration.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 SendgridCreateConfigurationSchemaType, {
+ type: "manual",
+ message,
+ });
+ }
+ }
+ 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
+ );
+ },
+ });
+
+ return (
+
+
+
+
+ Connect Sendgrid with Saleor.
+
+
+
+ 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.
+
+ }
+ >
+
+
+
+
+
+ );
+};
+
+export default NewSendgridConfigurationPage;
diff --git a/apps/emails-and-messages/src/pages/index.tsx b/apps/emails-and-messages/src/pages/index.tsx
index be3b801..2955309 100644
--- a/apps/emails-and-messages/src/pages/index.tsx
+++ b/apps/emails-and-messages/src/pages/index.tsx
@@ -3,7 +3,6 @@ import { useAppBridge } from "@saleor/app-sdk/app-bridge";
import { useEffect } from "react";
import { useIsMounted } from "usehooks-ts";
import { useRouter } from "next/router";
-import { LinearProgress } from "@material-ui/core";
import { isInIframe } from "../lib/is-in-iframe";
import { appName } from "../const";
@@ -14,12 +13,12 @@ const IndexPage: NextPage = () => {
useEffect(() => {
if (isMounted() && appBridgeState?.ready) {
- replace("/configuration/channels");
+ replace("/configuration");
}
}, [isMounted, appBridgeState?.ready, replace]);
if (isInIframe()) {
- return ;
+ return Loading
;
}
return (
diff --git a/apps/emails-and-messages/src/styles/globals.css b/apps/emails-and-messages/src/styles/globals.css
index 3a624b6..94c864c 100644
--- a/apps/emails-and-messages/src/styles/globals.css
+++ b/apps/emails-and-messages/src/styles/globals.css
@@ -1,21 +1,16 @@
body {
- font-family: Inter, -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
- "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
- color: #111;
+ color: var(--mu-colors-foreground-text-neutral-plain);
+ padding: 0;
+ margin: 0;
+ background: var(--mu-colors-background-plain);
}
code {
- background: #f6f8fa;
- border: 1px solid #eaeaea;
- border-radius: 5px;
display: inline-block;
- margin-top: 10px;
- padding: 0.75rem;
- font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
- Bitstream Vera Sans Mono, Courier New, monospace;
+ letter-spacing: 0.1em;
+ color: var(--mu-colors-foreground-text-neutral-subdued);
}
-code::before {
- content: "$ ";
- opacity: 0.6;
+a {
+ text-decoration: none;
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6b2000c..d91023a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -325,7 +325,7 @@ importers:
'@monaco-editor/react': ^4.4.6
'@saleor/app-sdk': 0.37.3
'@saleor/apps-shared': workspace:*
- '@saleor/macaw-ui': 0.8.0-pre.72
+ '@saleor/macaw-ui': 0.8.0-pre.76
'@sendgrid/client': ^7.7.0
'@sendgrid/mail': ^7.7.0
'@tanstack/react-query': ^4.24.4
@@ -375,7 +375,7 @@ importers:
'@monaco-editor/react': 4.4.6_biqbaboplfbrettd7655fr4n2y
'@saleor/app-sdk': 0.37.3_yucv4tfv7v7nrkw2uguegj6e7e
'@saleor/apps-shared': link:../../packages/shared
- '@saleor/macaw-ui': 0.8.0-pre.72_5ndqzdd6t4rivxsukjv3i3ak2q
+ '@saleor/macaw-ui': 0.8.0-pre.76_5ndqzdd6t4rivxsukjv3i3ak2q
'@sendgrid/client': 7.7.0
'@sendgrid/mail': 7.7.0
'@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y
@@ -4804,8 +4804,8 @@ packages:
- '@types/react'
dev: false
- /@saleor/macaw-ui/0.8.0-pre.72_5ndqzdd6t4rivxsukjv3i3ak2q:
- resolution: {integrity: sha512-9lcFkzf81q9Mxjqd00rWUUvom26YK3WCu8GCcmpqcEFu723/H76hxg2/LUd2cpqARavS1FgO+Vri7jkxkSz7sQ==}
+ /@saleor/macaw-ui/0.8.0-pre.76_5ndqzdd6t4rivxsukjv3i3ak2q:
+ resolution: {integrity: sha512-z5zlgdiLcJTR4al4FP6Z3JBzcH1VWQQRrUVH/TraqvHfIxC5XCPz1ZSve1/KUyXafbjUaM0ih81B9vqfipbXRA==}
engines: {node: '>=16 <19', pnpm: '>=8'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0