diff --git a/apps/emails-and-messages/package.json b/apps/emails-and-messages/package.json index 596a5de..a672ecc 100644 --- a/apps/emails-and-messages/package.json +++ b/apps/emails-and-messages/package.json @@ -16,13 +16,10 @@ "schemaVersion": "3.11.7" }, "dependencies": { - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", - "@material-ui/lab": "4.0.0-alpha.61", "@monaco-editor/react": "^4.4.6", "@saleor/app-sdk": "0.37.3", "@saleor/apps-shared": "workspace:*", - "@saleor/macaw-ui": "^0.7.2", + "@saleor/macaw-ui": "0.8.0-pre.72", "@sendgrid/client": "^7.7.0", "@sendgrid/mail": "^7.7.0", "@tanstack/react-query": "^4.24.4", diff --git a/apps/emails-and-messages/src/lib/theme-synchronizer.tsx b/apps/emails-and-messages/src/lib/theme-synchronizer.tsx index 7e5ce17..604dc26 100644 --- a/apps/emails-and-messages/src/lib/theme-synchronizer.tsx +++ b/apps/emails-and-messages/src/lib/theme-synchronizer.tsx @@ -1,33 +1,24 @@ import { useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { useTheme } from "@saleor/macaw-ui"; -import { memo, useEffect } from "react"; +import { useTheme } from "@saleor/macaw-ui/next"; +import { useEffect } from "react"; -/** - * Macaw-ui stores its theme mode in memory and local storage. To synchronize App with Dashboard, - * Macaw must be informed about this change from AppBridge. - * - * If you are not using Macaw, you can remove this. - */ -function _ThemeSynchronizer() { +export function ThemeSynchronizer() { const { appBridgeState } = useAppBridge(); - const { setTheme, themeType } = useTheme(); + const { setTheme } = useTheme(); useEffect(() => { if (!setTheme || !appBridgeState?.theme) { return; } - if (themeType !== appBridgeState?.theme) { - setTheme(appBridgeState.theme); - /** - * Hack to fix macaw, which is going into infinite loop on light mode (probably de-sync local storage with react state) - * TODO Fix me when Macaw 2.0 is shipped - */ - window.localStorage.setItem("macaw-ui-theme", appBridgeState.theme); + if (appBridgeState.theme === "light") { + setTheme("defaultLight"); } - }, [appBridgeState?.theme, setTheme, themeType]); + + if (appBridgeState.theme === "dark") { + setTheme("defaultDark"); + } + }, [appBridgeState?.theme, setTheme]); return null; } - -export const ThemeSynchronizer = memo(_ThemeSynchronizer); diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/instructions.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/instructions.tsx deleted file mode 100644 index e091880..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/instructions.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Paper, Typography } from "@material-ui/core"; -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles((theme) => { - return { - instructionsContainer: { - padding: 15, - }, - }; -}); - -export const Instructions = () => { - const styles = useStyles(); - - return ( - - - Welcome to Emails and Messages App! - - - The application will allow you to send emails and messages to your customers using different - services. - - - - How to configure the app - - - Start by creating a new configuration for provider of your choice. You can create multiple - configurations and then assign them to channels. Navigate to the relevant tab to configure - the provider. - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/provider-selection-box.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/provider-selection-box.tsx new file mode 100644 index 0000000..3124a9f --- /dev/null +++ b/apps/emails-and-messages/src/modules/app-configuration/ui/provider-selection-box.tsx @@ -0,0 +1,36 @@ +import { Box, Button, Text } from "@saleor/macaw-ui/next"; +import { BoxWithBorder } from "../../../components/box-with-border"; +import { defaultPadding } from "../../../components/ui-defaults"; +import { BoxFooter } from "../../../components/box-footer"; + +interface ProviderSelectionBoxProps { + providerName: string; + providerDescription: string; + onClick: () => void; +} + +export const ProviderSelectionBox = (props: ProviderSelectionBoxProps) => { + return ( + + + {props.providerName} + + + {props.providerDescription} + + + + + + ); +}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-form.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-form.tsx deleted file mode 100644 index f30bc02..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-form.tsx +++ /dev/null @@ -1,303 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { Divider, TextField, TextFieldProps, Typography } from "@material-ui/core"; -import { Button, makeStyles, SwitchSelector, SwitchSelectorButton } from "@saleor/macaw-ui"; -import React, { useEffect } from "react"; -import { MjmlConfiguration, smtpEncryptionTypes } from "../mjml-config"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useAppBridge, actions } from "@saleor/app-sdk/app-bridge"; -import { useQueryClient } from "@tanstack/react-query"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => ({ - field: { - marginBottom: 20, - }, - editor: { - marginBottom: 20, - }, - preview: { - marginBottom: 20, - }, - sectionHeader: { - marginTop: 20, - }, -})); - -type Props = { - onConfigurationSaved: () => void; - initialData: MjmlConfiguration; - configurationId?: string; -}; - -export const MjmlConfigurationForm = (props: Props) => { - const styles = useStyles(); - const { notifySuccess, notifyError } = useDashboardNotification(); - - const { handleSubmit, control, reset, setError } = useForm({ - defaultValues: props.initialData, - }); - - const queryClient = useQueryClient(); - - const { mutate: createOrUpdateConfiguration } = - trpcClient.mjmlConfiguration.updateOrCreateConfiguration.useMutation({ - onSuccess: async (data, variables) => { - await queryClient.cancelQueries({ queryKey: ["mjmlConfiguration", "getConfigurations"] }); - - // Optimistically update to the new value - queryClient.setQueryData>( - ["mjmlConfiguration", "getConfigurations", undefined], - (old) => { - if (old) { - const index = old.findIndex((c) => c.id === data.id); - // If thats an update, replace the old one - if (index !== -1) { - old[index] = data; - return [...old]; - } else { - return [...old, data]; - } - } else { - return [data]; - } - } - ); - - // Trigger refetch to make sure we have a fresh data - props.onConfigurationSaved(); - notifySuccess("Configuration saved"); - }, - 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 MjmlConfiguration, { - 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 - ); - }, - }); - - // when the configuration tab is changed, initialData change and form has to be updated - useEffect(() => { - reset(props.initialData); - }, [props.initialData, props.configurationId, reset]); - - const CommonFieldProps: TextFieldProps = { - className: styles.field, - fullWidth: true, - }; - - const isNewConfiguration = !props.configurationId; - - return ( -
{ - createOrUpdateConfiguration({ - ...data, - }); - })} - > - {isNewConfiguration ? ( - - Create a new configuration - - ) : ( - - Configuration - {` ${props.initialData.configurationName} `} - - )} - - ( - - )} - /> - - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(button.value)} - activeTab={value.toString()} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - - - - Sender details - - - ( - - )} - /> - - ( - <> - - - )} - /> - - - - - SMTP server configuration - - - ( - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for SwitchSelector */} - - {smtpEncryptionTypes.map((encryptionType) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(encryptionType)} - activeTab={value} - key={encryptionType} - > - {encryptionType === "NONE" ? "No encryption" : encryptionType} - - ))} - -
- )} - /> - - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx deleted file mode 100644 index 0ceef17..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React from "react"; -import { makeStyles } from "@saleor/macaw-ui"; -import { AppColumnsLayout } from "../../../ui/app-columns-layout"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { MjmlConfigurationForm } from "./mjml-configuration-form"; -import { getDefaultEmptyConfiguration } from "../mjml-config-container"; -import { NextRouter, useRouter } from "next/router"; -import { mjmlUrls } from "../../urls"; -import { MjmlTemplatesCard } from "./mjml-templates-card"; -import { MjmlConfiguration } from "../mjml-config"; -import { LoadingIndicator } from "../../../ui/loading-indicator"; -import { MjmlInstructions } from "./mjml-instructions"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => { - return { - formContainer: { - top: 0, - }, - instructionsContainer: { - padding: 15, - }, - configurationColumn: { - display: "flex", - flexDirection: "column", - gap: 20, - maxWidth: 600, - }, - }; -}); - -interface MjmlConfigurationTabProps { - configurationId?: string; -} - -const navigateToFirstConfiguration = (router: NextRouter, configurations?: MjmlConfiguration[]) => { - if (!configurations || !configurations?.length) { - router.replace(mjmlUrls.configuration()); - return; - } - const firstConfigurationId = configurations[0]?.id; - - if (firstConfigurationId) { - router.replace(mjmlUrls.configuration(firstConfigurationId)); - return; - } -}; - -export const MjmlConfigurationTab = ({ configurationId }: MjmlConfigurationTabProps) => { - const styles = useStyles(); - const router = useRouter(); - - const { - data: configurations, - refetch: refetchConfigurations, - isLoading: configurationsIsLoading, - isFetching: configurationsIsFetching, - } = trpcClient.mjmlConfiguration.getConfigurations.useQuery(undefined, { - onSuccess(data) { - if (!configurationId) { - console.log("no conf id! navigate to first"); - navigateToFirstConfiguration(router, data); - } - }, - }); - - if (configurationsIsLoading || configurationsIsFetching) { - return ; - } - - const configuration = configurations?.find((c) => c.id === configurationId?.toString()); - - if (configurationId && !configuration) { - return
Configuration not found
; - } - - return ( - -
- {configurationsIsLoading || configurationsIsFetching ? ( - - ) : ( - <> - refetchConfigurations()} - initialData={configuration || getDefaultEmptyConfiguration()} - configurationId={configurationId} - /> - {!!configurationId && !!configuration && ( - { - refetchConfigurations(); - }} - /> - )} - - )} -
- -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-event-configuration-form.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-event-configuration-form.tsx deleted file mode 100644 index 069a336..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-event-configuration-form.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { CircularProgress, Grid, TextField, TextFieldProps, Typography } from "@material-ui/core"; -import { - BackSmallIcon, - Button, - IconButton, - makeStyles, - SwitchSelector, - SwitchSelectorButton, -} from "@saleor/macaw-ui"; -import React, { useEffect, useState } from "react"; -import { MjmlEventConfiguration } from "../mjml-config"; -import { CodeEditor } from "../../../ui/code-editor"; -import { MjmlPreview } from "./mjml-preview"; -import { - MessageEventTypes, - messageEventTypesLabels, -} from "../../../event-handlers/message-event-types"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useDebounce } from "usehooks-ts"; -import { useRouter } from "next/router"; -import { mjmlUrls } from "../../urls"; -import { useAppBridge, actions } from "@saleor/app-sdk/app-bridge"; -import { examplePayloads } from "../../../event-handlers/default-payloads"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const PREVIEW_DEBOUNCE_DELAY = 500; - -const useStyles = makeStyles((theme) => ({ - viewContainer: { - padding: theme.spacing(2), - }, - header: { - display: "flex", - justifyContent: "flex-start", - alignItems: "center", - gap: theme.spacing(2), - marginBottom: theme.spacing(2), - margin: "0 auto", - }, - previewHeader: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - gap: theme.spacing(1), - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2), - }, - - field: { - marginBottom: theme.spacing(3), - }, - editor: { - marginBottom: theme.spacing(3), - }, - preview: { - marginBottom: theme.spacing(3), - }, - form: { - maxWidth: 800, - }, -})); - -type EventConfigurationFormProps = { - initialData: MjmlEventConfiguration; - configurationId: string; - eventType: MessageEventTypes; -}; - -export const EventConfigurationForm = ({ - initialData, - configurationId, - eventType, -}: EventConfigurationFormProps) => { - const { notifySuccess, notifyError } = useDashboardNotification(); - const router = useRouter(); - const { appBridge } = useAppBridge(); - const { handleSubmit, control, getValues, setError } = useForm({ - defaultValues: initialData, - }); - - const [lastValidRenderedTemplate, setLastValidRenderedTemplate] = useState(""); - - const [lastValidRenderedSubject, setLastValidRenderedSubject] = useState(""); - - const [payload, setPayload] = useState( - JSON.stringify(examplePayloads[eventType], undefined, 2) - ); - - const { template, subject } = getValues(); - const debouncedMutationVariables = useDebounce( - { template, subject, payload }, - PREVIEW_DEBOUNCE_DELAY - ); - - const styles = useStyles(); - - const CommonFieldProps: TextFieldProps = { - className: styles.field, - fullWidth: true, - }; - - const { mutate: fetchTemplatePreview, isLoading: isFetchingTemplatePreview } = - trpcClient.mjmlConfiguration.renderTemplate.useMutation({ - onSuccess: (data) => { - if (data.renderedEmailBody) { - setLastValidRenderedTemplate(data.renderedEmailBody); - } - if (data.renderedSubject) { - setLastValidRenderedSubject(data.renderedSubject); - } - }, - }); - - const { mutate: updateEventConfiguration } = - trpcClient.mjmlConfiguration.updateEventConfiguration.useMutation({ - onSuccess: (data) => { - notifySuccess("Success", "Configuration saved"); - }, - 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 MjmlEventConfiguration, { - 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 - ); - }, - }); - - const { - template: debouncedTemplate, - subject: debouncedSubject, - payload: debouncedPayload, - } = debouncedMutationVariables; - useEffect(() => { - fetchTemplatePreview({ - template: debouncedTemplate, - subject: debouncedSubject, - payload: debouncedPayload, - }); - }, [debouncedPayload, debouncedSubject, debouncedTemplate, fetchTemplatePreview]); - - return ( -
-
- { - router.push(mjmlUrls.configuration(configurationId)); - }} - > - - - - {messageEventTypesLabels[eventType]} event configuration - -
- - -
{ - updateEventConfiguration({ ...data, configurationId }); - })} - className={styles.form} - > - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(button.value)} - activeTab={value.toString()} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - ( - - )} - /> - - { - return ( - <> -
- -
- - ); - }} - /> - - -
- -
-
- Preview - {isFetchingTemplatePreview && } -
- - Subject: {lastValidRenderedSubject} - -
- -
- -
-
-
-
- ); -}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-instructions.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-instructions.tsx deleted file mode 100644 index c63b3ec..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-instructions.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Link, Paper, Typography } from "@material-ui/core"; -import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles((theme) => { - return { - instructionsContainer: { - padding: 15, - }, - }; -}); - -export const MjmlInstructions = () => { - const styles = useStyles(); - - const { appBridge } = useAppBridge(); - - return ( - - - MJML Provider - - - You can use this provider to send emails using MJML as a template language. The emails are - then sent using the SMTP. - - - - { - event.preventDefault(); - appBridge?.dispatch( - actions.Redirect({ - to: "https://mjml.io/", - newContext: true, - }) - ); - }} - > - Visit the MJML Homepage - - - - How to configure - - - Create a new configuration and fill in the required fields. After the configuration is - saved, you will be able to modify the email templates. - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-preview.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-preview.tsx deleted file mode 100644 index 8347fe3..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-preview.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Card } from "@material-ui/core"; -import React from "react"; - -type Props = { - value?: string; -}; - -export const MjmlPreview = ({ value }: Props) => { - return ( - - {value?.length ? ( -
- ) : ( -

No template preview

- )} - - ); -}; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-templates-card.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-templates-card.tsx deleted file mode 100644 index 91b18f1..0000000 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-templates-card.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { Divider, Paper, Typography } from "@material-ui/core"; -import React from "react"; -import { - EditIcon, - IconButton, - List, - ListHeader, - ListItem, - ListItemCell, - makeStyles, - SwitchSelector, - SwitchSelectorButton, -} from "@saleor/macaw-ui"; -import { useRouter } from "next/router"; -import { mjmlUrls } from "../../urls"; -import { messageEventTypesLabels } from "../../../event-handlers/message-event-types"; -import { MjmlConfiguration } from "../mjml-config"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => { - return { - spaceBetween: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - }, - rowActions: { - display: "flex", - justifyContent: "flex-end", - gap: theme.spacing(1), - }, - tableRow: { - minHeight: "48px", - "&::after": { - display: "none", - }, - }, - }; -}); - -interface MjmlTemplatesCardProps { - configurationId: string; - configuration: MjmlConfiguration; - onEventChanged: () => void; -} - -export const MjmlTemplatesCard = ({ - configurationId, - configuration, - onEventChanged, -}: MjmlTemplatesCardProps) => { - const classes = useStyles(); - const router = useRouter(); - const { notifySuccess } = useDashboardNotification(); - - const { mutate: updateEventConfiguration } = - trpcClient.mjmlConfiguration.updateEventConfiguration.useMutation({ - onSuccess(data, variables) { - onEventChanged(); - - notifySuccess(variables.active ? "Event enabled" : "Event disabled"); - }, - }); - - return ( - - - - Supported events and templates - - - - - {configuration.events.map((eventConfiguration) => ( - - - -
- {messageEventTypesLabels[eventConfiguration.eventType]} -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - { - updateEventConfiguration({ - configurationId, - ...eventConfiguration, - active: button.value, - }); - }} - activeTab={eventConfiguration.active.toString()} - key={button.label} - > - {button.label} - - ))} - - { - event.stopPropagation(); - event.preventDefault(); - router.push( - mjmlUrls.eventConfiguration(configurationId, eventConfiguration.eventType) - ); - }} - > - - -
-
-
-
- -
- ))} -
-
- ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-form.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-form.tsx deleted file mode 100644 index f717357..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-form.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { - Divider, - FormControl, - InputLabel, - MenuItem, - Select, - TextField, - TextFieldProps, - Typography, -} from "@material-ui/core"; -import { Button, makeStyles, SwitchSelector, SwitchSelectorButton } from "@saleor/macaw-ui"; -import React, { useEffect, useState } from "react"; -import { SendgridConfiguration } from "../sendgrid-config"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useAppBridge, actions } from "@saleor/app-sdk/app-bridge"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { fetchSenders } from "../../sendgrid-api"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => ({ - field: { - marginBottom: 20, - }, - editor: { - marginBottom: 20, - }, - preview: { - marginBottom: 20, - }, - sectionHeader: { - marginTop: 20, - }, -})); - -type Props = { - onConfigurationSaved: () => void; - initialData: SendgridConfiguration; - configurationId?: string; -}; - -export const SendgridConfigurationForm = (props: Props) => { - const styles = useStyles(); - const { appBridge } = useAppBridge(); - const { notifySuccess, notifyError } = useDashboardNotification(); - const [senderId, setSenderId] = useState(undefined); - - const { handleSubmit, control, reset, setError, setValue } = useForm({ - defaultValues: props.initialData, - }); - - const { data: sendersChoices, isLoading: isSendersChoicesLoading } = useQuery({ - queryKey: ["sendgridSenders"], - queryFn: fetchSenders({ apiKey: props.initialData.apiKey }), - enabled: !!props.initialData.apiKey?.length, - onSuccess(data) { - // we are not keeping senders ID in the database, so we need to find the ID of the sender - // configuration contains nickname and email set up in the Sendgrid account - if (data.length) { - const sender = data?.find((sender) => sender.from_email === props.initialData.senderEmail); - if (sender?.value) { - setSenderId(sender?.value); - } - } - }, - }); - - const queryClient = useQueryClient(); - - const { mutate: createOrUpdateConfiguration } = - trpcClient.sendgridConfiguration.updateOrCreateConfiguration.useMutation({ - onSuccess: async (data, variables) => { - await queryClient.cancelQueries({ - queryKey: ["sendgridConfiguration", "getConfigurations"], - }); - - // Optimistically update to the new value - queryClient.setQueryData>( - ["sendgridConfiguration", "getConfigurations", undefined], - (old) => { - if (old) { - const index = old.findIndex((c) => c.id === data.id); - // If thats an update, replace the old one - if (index !== -1) { - old[index] = data; - return [...old]; - } else { - return [...old, data]; - } - } else { - return [data]; - } - } - ); - - // Trigger refetch to make sure we have a fresh data - props.onConfigurationSaved(); - notifySuccess("Configuration saved"); - }, - 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 SendgridConfiguration, { - 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 - ); - }, - }); - - // when the configuration tab is changed, initialData change and form has to be updated - useEffect(() => { - reset(props.initialData); - }, [props.initialData, props.configurationId, reset]); - - // fill sender email and name when sender is changed - useEffect(() => { - const sender = sendersChoices?.find((choice) => choice.value === senderId); - if (sender) { - setValue("senderName", sender.nickname); - setValue("senderEmail", sender.from_email); - } else { - setValue("senderName", undefined); - setValue("senderEmail", undefined); - } - }, [senderId, sendersChoices]); - - const CommonFieldProps: TextFieldProps = { - className: styles.field, - fullWidth: true, - }; - - const isNewConfiguration = !props.configurationId; - - return ( -
{ - createOrUpdateConfiguration({ - ...data, - }); - })} - > - {isNewConfiguration ? ( - - Create a new configuration - - ) : ( - - Configuration - {` ${props.initialData.configurationName} `} - - )} - - ( - - )} - /> - - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(button.value)} - activeTab={value.toString()} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - - - - API configuration - - - ( - - )} - /> - - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Live", value: false }, - { label: "Sandbox", value: true }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(button.value)} - activeTab={value.toString()} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - - - {/* Sender can be chosen after the API key is saved in the configuration */} - {!isNewConfiguration && ( - <> - - Sender details - - - - Sender - - {!sendersChoices?.length && ( - - Please set up and verify senders in your Sendgrid dashboard. - - )} - - - ( - - )} - /> - - ( - <> - - - )} - /> - - )} - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-tab.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-tab.tsx deleted file mode 100644 index c78e1c4..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-configuration-tab.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from "react"; -import { makeStyles } from "@saleor/macaw-ui"; -import { AppColumnsLayout } from "../../../ui/app-columns-layout"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { SendgridConfigurationForm } from "./sendgrid-configuration-form"; -import { getDefaultEmptyConfiguration } from "../sendgrid-config-container"; -import { NextRouter, useRouter } from "next/router"; -import { SendgridConfiguration } from "../sendgrid-config"; -import { LoadingIndicator } from "../../../ui/loading-indicator"; -import { sendgridUrls } from "../../urls"; -import { SendgridTemplatesCard } from "./sendgrid-templates-card"; -import { SendgridInstructions } from "./sendgrid-instructions"; - -const useStyles = makeStyles((theme) => { - return { - formContainer: { - top: 0, - }, - instructionsContainer: { - padding: 15, - }, - configurationColumn: { - display: "flex", - flexDirection: "column", - gap: 20, - maxWidth: 700, - }, - }; -}); - -interface SendgridConfigurationTabProps { - configurationId?: string; -} - -const navigateToFirstConfiguration = ( - router: NextRouter, - configurations?: SendgridConfiguration[] -) => { - if (!configurations || !configurations?.length) { - router.replace(sendgridUrls.configuration()); - return; - } - const firstConfigurationId = configurations[0]?.id; - - if (firstConfigurationId) { - router.replace(sendgridUrls.configuration(firstConfigurationId)); - return; - } -}; - -export const SendgridConfigurationTab = ({ configurationId }: SendgridConfigurationTabProps) => { - const styles = useStyles(); - const router = useRouter(); - - const { - data: configurations, - refetch: refetchConfigurations, - isLoading: configurationsIsLoading, - isFetching: configurationsIsFetching, - } = trpcClient.sendgridConfiguration.getConfigurations.useQuery(undefined, { - onSuccess(data) { - if (!configurationId) { - console.log("no conf id! navigate to first"); - navigateToFirstConfiguration(router, data); - } - }, - }); - - if (configurationsIsLoading || configurationsIsFetching) { - return ; - } - - const configuration = configurations?.find((c) => c.id === configurationId?.toString()); - - if (configurationId && !configuration) { - return
Configuration not found
; - } - - return ( - -
- {configurationsIsLoading || configurationsIsFetching ? ( - - ) : ( - <> - refetchConfigurations()} - initialData={configuration || getDefaultEmptyConfiguration()} - configurationId={configurationId} - /> - {!!configurationId && !!configuration && ( - { - refetchConfigurations(); - }} - /> - )} - - )} -
- -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-event-configuration-form.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-event-configuration-form.tsx deleted file mode 100644 index fa19867..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-event-configuration-form.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { - FormControl, - Grid, - InputLabel, - MenuItem, - Select, - TextFieldProps, - Typography, -} from "@material-ui/core"; -import { - BackSmallIcon, - Button, - IconButton, - makeStyles, - SwitchSelector, - SwitchSelectorButton, -} from "@saleor/macaw-ui"; -import React from "react"; -import { SendgridConfiguration, SendgridEventConfiguration } from "../sendgrid-config"; -import { - MessageEventTypes, - messageEventTypesLabels, -} from "../../../event-handlers/message-event-types"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useRouter } from "next/router"; -import { sendgridUrls } from "../../urls"; -import { useQuery } from "@tanstack/react-query"; -import { fetchTemplates } from "../../sendgrid-api"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => ({ - viewContainer: { - padding: theme.spacing(2), - }, - header: { - display: "flex", - justifyContent: "flex-start", - alignItems: "center", - gap: theme.spacing(2), - marginBottom: theme.spacing(2), - margin: "0 auto", - }, - previewHeader: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - gap: theme.spacing(1), - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2), - }, - - field: { - marginBottom: theme.spacing(3), - }, - editor: { - marginBottom: theme.spacing(3), - }, - preview: { - marginBottom: theme.spacing(3), - }, - form: { - maxWidth: 800, - }, -})); - -type EventConfigurationFormProps = { - initialData: SendgridEventConfiguration; - configurationId: string; - eventType: MessageEventTypes; - configuration: SendgridConfiguration; -}; - -export const EventConfigurationForm = ({ - initialData, - configurationId, - eventType, - configuration, -}: EventConfigurationFormProps) => { - const router = useRouter(); - const { notifySuccess, notifyError } = useDashboardNotification(); - - const { handleSubmit, control, getValues, setError } = useForm({ - defaultValues: initialData, - }); - - const styles = useStyles(); - - const { data: templateChoices, isLoading: isTemplateChoicesLoading } = useQuery({ - queryKey: ["sendgridTemplates"], - queryFn: fetchTemplates({ apiKey: configuration.apiKey }), - enabled: !!configuration.apiKey?.length, - }); - - const CommonFieldProps: TextFieldProps = { - className: styles.field, - fullWidth: true, - }; - - const { mutate: updateEventConfiguration } = - trpcClient.sendgridConfiguration.updateEventConfiguration.useMutation({ - onSuccess: (data) => { - notifySuccess("Configuration saved"); - }, - 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 SendgridEventConfiguration, { - 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 ( -
-
- { - router.push(sendgridUrls.configuration(configurationId)); - }} - > - - - - {messageEventTypesLabels[eventType]} event configuration - -
- - -
{ - updateEventConfiguration({ ...data, configurationId }); - })} - className={styles.form} - > - ( -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - onChange(button.value)} - activeTab={value.toString()} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - { - return ( - - Template - - {!templateChoices?.length && ( - - No templates found in your account. Visit Sendgrid dashboard and create one. - - )} - - ); - }} - /> - - - -
-
-
- ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-instructions.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-instructions.tsx deleted file mode 100644 index bfcb643..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-instructions.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { Link, Paper, Typography } from "@material-ui/core"; -import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles((theme) => { - return { - instructionsContainer: { - padding: 15, - }, - }; -}); - -export const SendgridInstructions = () => { - const styles = useStyles(); - - const { appBridge } = useAppBridge(); - - return ( - - - Sendgrid Provider - - - The integration uses dynamic email templates to send the messages to your customers. - - - - { - event.preventDefault(); - appBridge?.dispatch( - actions.Redirect({ - to: "https://sendgrid.com/", - newContext: true, - }) - ); - }} - > - Visit the Sendgrid Homepage - - - - How to configure - - - - Before configuring the app, make sure you have a Sendgrid account set up. To proceed you - will need: -
- { - event.preventDefault(); - appBridge?.dispatch( - actions.Redirect({ - to: "https://app.sendgrid.com/settings/api_keys", - newContext: true, - }) - ); - }} - > - API key which can be generated in the Sendgrid dashboard - -
- { - event.preventDefault(); - appBridge?.dispatch( - actions.Redirect({ - to: "https://app.sendgrid.com/settings/sender_auth", - newContext: true, - }) - ); - }} - > - Verified sender account - -
- { - event.preventDefault(); - appBridge?.dispatch( - actions.Redirect({ - to: "https://mc.sendgrid.com/dynamic-templates", - newContext: true, - }) - ); - }} - > - Created dynamic email templates - -
- - - Create a new configuration and fill in the required fields. After the configuration is - saved, you will be able to assign the email template to each of the events. - -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-templates-card.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-templates-card.tsx deleted file mode 100644 index 0b0ca85..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/sendgrid-templates-card.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { Divider, Paper, Typography } from "@material-ui/core"; -import React from "react"; -import { - EditIcon, - IconButton, - List, - ListHeader, - ListItem, - ListItemCell, - makeStyles, - SwitchSelector, - SwitchSelectorButton, -} from "@saleor/macaw-ui"; -import { useRouter } from "next/router"; -import { messageEventTypesLabels } from "../../../event-handlers/message-event-types"; -import { trpcClient } from "../../../trpc/trpc-client"; -import { useAppBridge, actions } from "@saleor/app-sdk/app-bridge"; -import { SendgridConfiguration } from "../sendgrid-config"; -import { sendgridUrls } from "../../urls"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => { - return { - spaceBetween: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - }, - rowActions: { - display: "flex", - justifyContent: "flex-end", - gap: theme.spacing(1), - }, - tableRow: { - minHeight: "48px", - "&::after": { - display: "none", - }, - }, - }; -}); - -interface SendgridTemplatesCardProps { - configurationId: string; - configuration: SendgridConfiguration; - onEventChanged: () => void; -} - -export const SendgridTemplatesCard = ({ - configurationId, - configuration, - onEventChanged, -}: SendgridTemplatesCardProps) => { - const classes = useStyles(); - const router = useRouter(); - const { notifySuccess } = useDashboardNotification(); - - const { mutate: updateEventConfiguration } = - trpcClient.sendgridConfiguration.updateEventConfiguration.useMutation({ - onSuccess(_data, variables) { - onEventChanged(); - notifySuccess(variables.active ? "Event enabled" : "Event disabled"); - }, - }); - - return ( - - - - Supported events and templates - - - - - {configuration.events.map((eventConfiguration) => ( - - - -
- {messageEventTypesLabels[eventConfiguration.eventType]} -
- {/* TODO: fix types in the MacawUI */} - {/* @ts-ignore: MacawUI use wrong type for */} - - {[ - { label: "Active", value: true }, - { label: "Disabled", value: false }, - ].map((button) => ( - // @ts-ignore: MacawUI use wrong type for SwitchSelectorButton - { - updateEventConfiguration({ - configurationId, - ...eventConfiguration, - active: button.value, - }); - }} - activeTab={eventConfiguration.active.toString()} - key={button.label} - > - {button.label} - - ))} - - { - event.stopPropagation(); - event.preventDefault(); - router.push( - sendgridUrls.eventConfiguration( - configurationId, - eventConfiguration.eventType - ) - ); - }} - > - - -
-
-
-
- -
- ))} -
-
- ); -}; diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/template-selection-field.tsx b/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/template-selection-field.tsx deleted file mode 100644 index 5768b1d..0000000 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/ui/template-selection-field.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { MenuItem, Select } from "@material-ui/core"; - -interface TemplateSelectionFieldProps { - templateChoices?: { label: string; value: string }[]; - value?: string; - onChange: (value: unknown) => void; -} - -export const TemplateSelectionField = ({ - value, - onChange, - templateChoices, -}: TemplateSelectionFieldProps) => { - return ( - - ); -}; diff --git a/apps/emails-and-messages/src/modules/ui/app-columns-layout.tsx b/apps/emails-and-messages/src/modules/ui/app-columns-layout.tsx deleted file mode 100644 index ca26174..0000000 --- a/apps/emails-and-messages/src/modules/ui/app-columns-layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { makeStyles } from "@saleor/macaw-ui"; -import { PropsWithChildren } from "react"; - -const useStyles = makeStyles((theme) => ({ - root: { - display: "grid", - gridTemplateColumns: "auto 400px", - alignItems: "start", - gap: theme.spacing(3), - padding: "20px 0", - }, -})); - -type AppColumnsLayoutProps = PropsWithChildren<{}>; - -export const AppColumnsLayout = ({ children }: AppColumnsLayoutProps) => { - const styles = useStyles(); - - return
{children}
; -}; diff --git a/apps/emails-and-messages/src/modules/ui/code-editor.tsx b/apps/emails-and-messages/src/modules/ui/code-editor.tsx deleted file mode 100644 index d8d0947..0000000 --- a/apps/emails-and-messages/src/modules/ui/code-editor.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useCallback, useRef } from "react"; - -import Editor from "@monaco-editor/react"; -import { useTheme } from "@saleor/macaw-ui"; - -type Props = { - onChange(value: string): void; - initialTemplate: string; - value: string; - language: string; -}; - -export const CodeEditor = ({ initialTemplate, onChange, value, language }: Props) => { - const { themeType } = useTheme(); - const editorRef = useRef(null); - - // @ts-ignore - function handleEditorDidMount(editor, monaco) { - editorRef.current = editor; - } - - const handleOnChange = useCallback( - (value?: string) => { - onChange(value ?? ""); - }, - [value] - ); - - return ( - <> - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/ui/configuration-page-base-layout.tsx b/apps/emails-and-messages/src/modules/ui/configuration-page-base-layout.tsx deleted file mode 100644 index 06b9b19..0000000 --- a/apps/emails-and-messages/src/modules/ui/configuration-page-base-layout.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { PropsWithChildren } from "react"; -import { makeStyles, PageTab, PageTabs } from "@saleor/macaw-ui"; -import { useRouter } from "next/router"; - -const useStyles = makeStyles((theme) => ({ - appContainer: { - marginTop: theme.spacing(3), - marginLeft: theme.spacing(3), - }, -})); - -type Props = PropsWithChildren<{}>; - -export const ConfigurationPageBaseLayout = ({ children }: Props) => { - const styles = useStyles(); - - const router = useRouter(); - const tabs = [ - { - key: "channels", - label: "Channels", - url: "/configuration/channels", - }, - { key: "mjml", label: "MJML", url: "/configuration/mjml" }, - { - key: "sendgrid", - label: "Sendgrid", - url: "/configuration/sendgrid", - }, - ]; - - const activePath = tabs.find((tab) => router.pathname.startsWith(tab.url))?.key; - - const navigateToTab = (value: string) => { - const redirectionUrl = tabs.find((tab) => tab.key === value)?.url; - if (redirectionUrl) { - router.push(redirectionUrl); - } - }; - return ( -
- - {tabs.map((tab) => ( - - ))} - - {children} -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/ui/loading-indicator.tsx b/apps/emails-and-messages/src/modules/ui/loading-indicator.tsx deleted file mode 100644 index 982a90a..0000000 --- a/apps/emails-and-messages/src/modules/ui/loading-indicator.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { CircularProgress } from "@material-ui/core"; -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles((theme) => { - return { - loaderContainer: { - margin: "50px auto", - display: "flex", - alignItems: "center", - justifyContent: "center", - }, - }; -}); - -export const LoadingIndicator = () => { - const styles = useStyles(); - return ( -
- -
- ); -}; diff --git a/apps/emails-and-messages/src/not-ready.tsx b/apps/emails-and-messages/src/not-ready.tsx index daa4739..d13a713 100644 --- a/apps/emails-and-messages/src/not-ready.tsx +++ b/apps/emails-and-messages/src/not-ready.tsx @@ -1,6 +1,4 @@ -import { AlertBase, Button } from "@saleor/macaw-ui"; import React from "react"; -import { Typography } from "@material-ui/core"; import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; import { appName } from "./const"; @@ -10,26 +8,7 @@ export const NotReadyPage = () => { return (

{appName}

- - - App can not be used - - - To configure the app you need to create at least 1 channel - - - + App can not be used
); }; diff --git a/apps/emails-and-messages/src/pages/configuration/channels/index.tsx b/apps/emails-and-messages/src/pages/configuration/channels/index.tsx deleted file mode 100644 index adbcf77..0000000 --- a/apps/emails-and-messages/src/pages/configuration/channels/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { NextPage } from "next"; -import React, { useEffect } from "react"; -import { useRouter } from "next/router"; -import { trpcClient } from "../../../modules/trpc/trpc-client"; -import { ConfigurationPageBaseLayout } from "../../../modules/ui/configuration-page-base-layout"; - -const ChannelsConfigurationPage: NextPage = () => { - const channels = trpcClient.channels.fetch.useQuery(); - const router = useRouter(); - - const sendgridConfigurations = trpcClient.sendgridConfiguration.getConfigurations.useQuery(); - const mjmlConfigurations = trpcClient.mjmlConfiguration.getConfigurations.useQuery(); - - useEffect(() => { - if (router && channels.isSuccess && channels.data.length === 0) { - router.push("/not-ready"); - } - }, [channels.data, channels.isSuccess, router]); - return ( - - Sendgrid configurations: -
    - {sendgridConfigurations.data?.map((c) => ( -
  • {c.configurationName}
  • - ))} -
- MJML configurations: -
    - {mjmlConfigurations.data?.map((c) => ( -
  • {c.configurationName}
  • - ))} -
-
- ); -}; - -export default ChannelsConfigurationPage; diff --git a/apps/emails-and-messages/src/pages/configuration/mjml/[[...configurationId]].tsx b/apps/emails-and-messages/src/pages/configuration/mjml/[[...configurationId]].tsx deleted file mode 100644 index 0a76a17..0000000 --- a/apps/emails-and-messages/src/pages/configuration/mjml/[[...configurationId]].tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { NextPage } from "next"; -import React from "react"; -import { useRouter } from "next/router"; -import { ConfigurationPageBaseLayout } from "../../../modules/ui/configuration-page-base-layout"; -import { MjmlConfigurationTab } from "../../../modules/mjml/configuration/ui/mjml-configuration-tab"; - -const MjmlConfigurationPage: NextPage = () => { - const router = useRouter(); - const configurationId = router.query.configurationId - ? router.query.configurationId[0] // optional routes are passed as an array - : undefined; - return ( - - - - ); -}; - -export default MjmlConfigurationPage; diff --git a/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx b/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx deleted file mode 100644 index 776b8e6..0000000 --- a/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { NextPage } from "next"; -import React from "react"; -import { useRouter } from "next/router"; -import { trpcClient } from "../../../../../modules/trpc/trpc-client"; - -import { parseMessageEventType } from "../../../../../modules/event-handlers/parse-message-event-type"; -import { ConfigurationPageBaseLayout } from "../../../../../modules/ui/configuration-page-base-layout"; -import { EventConfigurationForm } from "../../../../../modules/mjml/configuration/ui/mjml-event-configuration-form"; -import { LoadingIndicator } from "../../../../../modules/ui/loading-indicator"; - -const EventConfigurationPage: NextPage = () => { - const router = useRouter(); - - const configurationId = router.query.configurationId as string; - const eventTypeFromQuery = router.query.eventType as string | undefined; - const eventType = parseMessageEventType(eventTypeFromQuery); - - const { - data: configuration, - isError, - isFetched, - isLoading, - error, - } = trpcClient.mjmlConfiguration.getEventConfiguration.useQuery( - { - configurationId, - // if event type is not valid, it calling the query will not be enabled - // so we can safely cast it - eventType: eventType!, - }, - { - enabled: !!configurationId && !!eventType, - } - ); - - // TODO: better error messages - if (!eventType || !configurationId) { - return <>Error: no event type or configuration id; - } - if (isLoading) { - return ( - - - - ); - } - - if (isError) { - return ( - <> - Error: could not load the config: fetched: {isFetched} is error {isError} - - ); - } - if (!configuration) { - return <>Error: no configuration with given id; - } - return ( - - - - ); -}; - -export default EventConfigurationPage; diff --git a/apps/emails-and-messages/src/pages/configuration/sendgrid/[[...configurationId]].tsx b/apps/emails-and-messages/src/pages/configuration/sendgrid/[[...configurationId]].tsx deleted file mode 100644 index 203aa0b..0000000 --- a/apps/emails-and-messages/src/pages/configuration/sendgrid/[[...configurationId]].tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { NextPage } from "next"; -import React from "react"; -import { useRouter } from "next/router"; -import { ConfigurationPageBaseLayout } from "../../../modules/ui/configuration-page-base-layout"; -import { SendgridConfigurationTab } from "../../../modules/sendgrid/configuration/ui/sendgrid-configuration-tab"; - -const SendgridConfigurationPage: NextPage = () => { - const router = useRouter(); - const configurationId = router.query.configurationId - ? router.query.configurationId[0] // optional routes are passed as an array - : undefined; - return ( - - - - ); -}; - -export default SendgridConfigurationPage; diff --git a/apps/emails-and-messages/src/pages/configuration/sendgrid/[configurationId]/event/[eventType].tsx b/apps/emails-and-messages/src/pages/configuration/sendgrid/[configurationId]/event/[eventType].tsx deleted file mode 100644 index 122c0e7..0000000 --- a/apps/emails-and-messages/src/pages/configuration/sendgrid/[configurationId]/event/[eventType].tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { NextPage } from "next"; -import React from "react"; -import { useRouter } from "next/router"; -import { trpcClient } from "../../../../../modules/trpc/trpc-client"; - -import { parseMessageEventType } from "../../../../../modules/event-handlers/parse-message-event-type"; -import { ConfigurationPageBaseLayout } from "../../../../../modules/ui/configuration-page-base-layout"; -import { LoadingIndicator } from "../../../../../modules/ui/loading-indicator"; -import { EventConfigurationForm } from "../../../../../modules/sendgrid/configuration/ui/sendgrid-event-configuration-form"; - -const EventConfigurationPage: NextPage = () => { - const router = useRouter(); - - const configurationId = router.query.configurationId as string; - const eventTypeFromQuery = router.query.eventType as string | undefined; - const eventType = parseMessageEventType(eventTypeFromQuery); - - const { - data: eventConfiguration, - isError, - isFetched, - isLoading, - } = trpcClient.sendgridConfiguration.getEventConfiguration.useQuery( - { - configurationId, - // if event type is not valid, it calling the query will not be enabled - // so we can safely cast it - eventType: eventType!, - }, - { - enabled: !!configurationId && !!eventType, - } - ); - - const { data: configuration } = trpcClient.sendgridConfiguration.getConfiguration.useQuery( - { - id: configurationId, - }, - { - enabled: !!configurationId, - } - ); - - if (!eventType || !configurationId) { - return <>Error: no event type or configuration id; - } - if (isLoading) { - return ( - - - - ); - } - - if (isError) { - return ( - <> - Error: could not load the config: fetched: {isFetched} is error {isError} - - ); - } - if (!eventConfiguration || !configuration) { - return <>Error: no configuration with given id; - } - return ( - - - - ); -}; - -export default EventConfigurationPage; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3724b32..6b2000c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -322,13 +322,10 @@ importers: '@graphql-codegen/typescript-urql': ^3.7.3 '@graphql-codegen/urql-introspection': 2.2.1 '@graphql-typed-document-node/core': ^3.1.2 - '@material-ui/core': ^4.12.4 - '@material-ui/icons': ^4.11.3 - '@material-ui/lab': 4.0.0-alpha.61 '@monaco-editor/react': ^4.4.6 '@saleor/app-sdk': 0.37.3 '@saleor/apps-shared': workspace:* - '@saleor/macaw-ui': ^0.7.2 + '@saleor/macaw-ui': 0.8.0-pre.72 '@sendgrid/client': ^7.7.0 '@sendgrid/mail': ^7.7.0 '@tanstack/react-query': ^4.24.4 @@ -375,13 +372,10 @@ importers: vitest: ^0.30.1 zod: ^3.20.2 dependencies: - '@material-ui/core': 4.12.4_5ndqzdd6t4rivxsukjv3i3ak2q - '@material-ui/icons': 4.11.3_x54wk6dsnsxe7g7vvfmytp77te - '@material-ui/lab': 4.0.0-alpha.61_x54wk6dsnsxe7g7vvfmytp77te '@monaco-editor/react': 4.4.6_biqbaboplfbrettd7655fr4n2y '@saleor/app-sdk': 0.37.3_yucv4tfv7v7nrkw2uguegj6e7e '@saleor/apps-shared': link:../../packages/shared - '@saleor/macaw-ui': 0.7.2_pmlnlm755hlzzzocw2qhf3a34e + '@saleor/macaw-ui': 0.8.0-pre.72_5ndqzdd6t4rivxsukjv3i3ak2q '@sendgrid/client': 7.7.0 '@sendgrid/mail': 7.7.0 '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y @@ -4320,6 +4314,34 @@ packages: - '@types/react' dev: false + /@radix-ui/react-popover/1.0.5_5ndqzdd6t4rivxsukjv3i3ak2q: + resolution: {integrity: sha512-GRHZ8yD12MrN2NLobHPE8Rb5uHTxd9x372DE9PPNnBjpczAQHcZ5ne0KXG4xpf+RDdXSzdLv9ym6mYJCDTaUZg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.1_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + aria-hidden: 1.2.2_3stiutgnnbnfnf3uowm5cip22i + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.5_3stiutgnnbnfnf3uowm5cip22i + transitivePeerDependencies: + - '@types/react' + dev: false + /@radix-ui/react-popper/1.1.1_5ndqzdd6t4rivxsukjv3i3ak2q: resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} peerDependencies: @@ -4479,6 +4501,31 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /@radix-ui/react-tooltip/1.0.5_5ndqzdd6t4rivxsukjv3i3ak2q: + resolution: {integrity: sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.1_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-visually-hidden': 1.0.2_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + /@radix-ui/react-use-callback-ref/1.0.0_react@18.2.0: resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} peerDependencies: @@ -4757,6 +4804,38 @@ packages: - '@types/react' dev: false + /@saleor/macaw-ui/0.8.0-pre.72_5ndqzdd6t4rivxsukjv3i3ak2q: + resolution: {integrity: sha512-9lcFkzf81q9Mxjqd00rWUUvom26YK3WCu8GCcmpqcEFu723/H76hxg2/LUd2cpqARavS1FgO+Vri7jkxkSz7sQ==} + engines: {node: '>=16 <19', pnpm: '>=8'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@dessert-box/react': 0.4.0_react@18.2.0 + '@floating-ui/react-dom-interactions': 0.5.0_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-accordion': 1.1.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-checkbox': 1.0.3_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-dialog': 1.0.3_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-dropdown-menu': 2.0.4_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-popover': 1.0.5_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-radio-group': 1.1.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-select': 1.2.1_5ndqzdd6t4rivxsukjv3i3ak2q + '@radix-ui/react-toggle': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-tooltip': 1.0.5_5ndqzdd6t4rivxsukjv3i3ak2q + '@vanilla-extract/css-utils': 0.1.3 + clsx: 1.2.1 + downshift: 6.1.12_react@18.2.0 + downshift7: /downshift/7.6.0_react@18.2.0 + lodash: 4.17.21 + lodash-es: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-inlinesvg: 3.0.1_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + /@selderee/plugin-htmlparser2/0.10.0: resolution: {integrity: sha512-gW69MEamZ4wk1OsOq1nG1jcyhXIQcnrsX5JwixVw/9xaiav8TCyjESAruu1Rz9yyInhgBXxkNwMeygKnN2uxNA==} dependencies: @@ -6412,6 +6491,10 @@ packages: graphql: 16.6.0 dev: true + /@vanilla-extract/css-utils/0.1.3: + resolution: {integrity: sha512-PZAcHROlgtCUGI2y0JntdNwvPwCNyeVnkQu6KTYKdmxBbK3w72XJUmLFYapfaFfgami4I9CTLnrJTPdtmS3gpw==} + dev: false + /@vitejs/plugin-react/3.1.0_vite@4.2.1: resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} @@ -7741,6 +7824,10 @@ packages: /compute-scroll-into-view/1.0.20: resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + /compute-scroll-into-view/2.0.4: + resolution: {integrity: sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g==} + dev: false + /computed-style/0.1.4: resolution: {integrity: sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==} dev: false @@ -8295,6 +8382,19 @@ packages: react-is: 17.0.2 tslib: 2.5.0 + /downshift/7.6.0_react@18.2.0: + resolution: {integrity: sha512-VSoTVynTAsabou/hbZ6HJHUVhtBiVOjQoBsCPcQq5eAROIGP+9XKMp9asAKQ3cEcUP4oe0fFdD2pziUjhFY33Q==} + peerDependencies: + react: '>=16.12.0' + dependencies: + '@babel/runtime': 7.20.13 + compute-scroll-into-view: 2.0.4 + prop-types: 15.8.1 + react: 18.2.0 + react-is: 17.0.2 + tslib: 2.5.0 + dev: false + /dset/3.1.2: resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} engines: {node: '>=4'}