From 55b998a75fccbbd6507c85b9dee68c3953e27b78 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Wed, 19 Apr 2023 18:13:33 +0200 Subject: [PATCH] Remove channels config --- .../src/lib/is-available-in-channel.ts | 27 +++ .../app-configuration/app-config-container.ts | 33 ---- .../app-config-input-schema.ts | 20 -- .../modules/app-configuration/app-config.ts | 11 -- .../app-configuration.router.ts | 70 ------- .../app-configuration/app-configurator.ts | 35 ---- .../app-configuration/fallback-app-config.ts | 22 --- .../get-app-configuration.service.ts | 95 --------- .../ui/app-configuration-form.tsx | 181 ------------------ .../ui/channels-configuration-tab.tsx | 146 -------------- .../app-configuration/ui/channels-list.tsx | 72 ------- .../ui/configurations-list.tsx | 87 --------- .../app-configuration/ui/side-menu.tsx | 109 ----------- .../event-handlers/send-event-messages.ts | 91 ++++----- .../configuration/mjml-config-container.ts | 25 ++- .../configuration/mjml-config-input-schema.ts | 4 + .../modules/mjml/configuration/mjml-config.ts | 4 + .../ui/mjml-configuration-tab.tsx | 60 +----- .../sendgrid-config-container.ts | 25 ++- .../sendgrid-config-input-schema.ts | 4 + .../sendgrid/configuration/sendgrid-config.ts | 4 + .../ui/sendgrid-configuration-tab.tsx | 64 +------ .../src/modules/trpc/trpc-app-router.ts | 2 - .../src/modules/ui/app-columns-layout.tsx | 2 +- .../pages/configuration/channels/index.tsx | 17 +- .../queries/FetchProductDataForFeed.graphql | 2 +- 26 files changed, 144 insertions(+), 1068 deletions(-) create mode 100644 apps/emails-and-messages/src/lib/is-available-in-channel.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/app-config-container.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/app-config.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/app-configurator.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/fallback-app-config.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/ui/channels-list.tsx delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/ui/configurations-list.tsx delete mode 100644 apps/emails-and-messages/src/modules/app-configuration/ui/side-menu.tsx diff --git a/apps/emails-and-messages/src/lib/is-available-in-channel.ts b/apps/emails-and-messages/src/lib/is-available-in-channel.ts new file mode 100644 index 0000000..f26a38b --- /dev/null +++ b/apps/emails-and-messages/src/lib/is-available-in-channel.ts @@ -0,0 +1,27 @@ +interface IsAvailableInChannelArgs { + channel: string; + restrictedToChannels: string[]; + excludedChannels: string[]; +} + +/** + * Returns true if the channel is available for the configuration. + * + * Is available if: + * - it's not in the excluded list + * - if assigned list is not empty, it's in the assigned list + * - assigned list is empty + */ +export const isAvailableInChannel = ({ + channel, + restrictedToChannels, + excludedChannels, +}: IsAvailableInChannelArgs): boolean => { + if (channel in excludedChannels) { + return false; + } + if (restrictedToChannels.length > 0 && !(channel in restrictedToChannels)) { + return false; + } + return true; +}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-config-container.ts b/apps/emails-and-messages/src/modules/app-configuration/app-config-container.ts deleted file mode 100644 index 7b1c9b7..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/app-config-container.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { AppConfig, AppConfigurationPerChannel } from "./app-config"; - -export const getDefaultEmptyAppConfiguration = (): AppConfigurationPerChannel => ({ - active: false, - mjmlConfigurationId: undefined, - sendgridConfigurationId: undefined, -}); - -const getChannelAppConfiguration = - (appConfig: AppConfig | null | undefined) => (channelSlug: string) => { - try { - return appConfig?.configurationsPerChannel[channelSlug] ?? null; - } catch (e) { - return null; - } - }; - -const setChannelAppConfiguration = - (appConfig: AppConfig | null | undefined) => - (channelSlug: string) => - (appConfiguration: AppConfigurationPerChannel) => { - const appConfigNormalized = structuredClone(appConfig) ?? { configurationsPerChannel: {} }; - - appConfigNormalized.configurationsPerChannel[channelSlug] ??= getDefaultEmptyAppConfiguration(); - appConfigNormalized.configurationsPerChannel[channelSlug] = appConfiguration; - - return appConfigNormalized; - }; - -export const AppConfigContainer = { - getChannelAppConfiguration, - setChannelAppConfiguration, -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts b/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts deleted file mode 100644 index f0b2227..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { z } from "zod"; - -export const appConfigInputSchema = z.object({ - configurationsPerChannel: z.record( - z.object({ - active: z.boolean(), - mjmlConfigurationId: z.string().optional(), - sendgridConfigurationId: z.string().optional(), - }) - ), -}); - -export const appChannelConfigurationInputSchema = z.object({ - channel: z.string(), - configuration: z.object({ - active: z.boolean(), - mjmlConfigurationId: z.string().optional(), - sendgridConfigurationId: z.string().optional(), - }), -}); diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-config.ts b/apps/emails-and-messages/src/modules/app-configuration/app-config.ts deleted file mode 100644 index d73fc49..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/app-config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AppConfigurationPerChannel { - active: boolean; - mjmlConfigurationId?: string; - sendgridConfigurationId?: string; -} - -export type AppConfigurationsChannelMap = Record; - -export type AppConfig = { - configurationsPerChannel: AppConfigurationsChannelMap; -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts b/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts deleted file mode 100644 index 853d9bc..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createLogger } from "@saleor/apps-shared"; -import { - appChannelConfigurationInputSchema, - appConfigInputSchema, -} from "./app-config-input-schema"; -import { AppConfigurationService } from "./get-app-configuration.service"; -import { router } from "../trpc/trpc-server"; -import { protectedClientProcedure } from "../trpc/protected-client-procedure"; -import { z } from "zod"; - -/* - * Allow access only for the dashboard users and attaches the - * configuration service to the context - */ -const protectedWithConfigurationService = protectedClientProcedure.use(({ next, ctx }) => - next({ - ctx: { - ...ctx, - configurationService: new AppConfigurationService({ - apiClient: ctx.apiClient, - saleorApiUrl: ctx.saleorApiUrl, - }), - }, - }) -); - -export const appConfigurationRouter = router({ - getChannelConfiguration: protectedWithConfigurationService - .input(z.object({ channelSlug: z.string() })) - .query(async ({ ctx, input }) => { - const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); - - logger.debug("Get Channel Configuration called"); - - return await ctx.configurationService.getChannelConfiguration(input.channelSlug); - }), - - setChannelConfiguration: protectedWithConfigurationService - .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) - .input(appChannelConfigurationInputSchema) - .mutation(async ({ ctx, input }) => { - const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); - - logger.debug("Set channel configuration called"); - - await ctx.configurationService.setChannelConfiguration(input); - }), - fetch: protectedWithConfigurationService.query(async ({ ctx, input }) => { - const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); - - logger.debug("appConfigurationRouter.fetch called"); - - return new AppConfigurationService({ - apiClient: ctx.apiClient, - saleorApiUrl: ctx.saleorApiUrl, - }).getConfiguration(); - }), - setAndReplace: protectedWithConfigurationService - .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) - .input(appConfigInputSchema) - .mutation(async ({ ctx, input }) => { - const logger = createLogger({ saleorApiUrl: ctx.saleorApiUrl }); - - logger.debug(input, "appConfigurationRouter.setAndReplace called with input"); - - await ctx.configurationService.setConfigurationRoot(input); - - return null; - }), -}); diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-configurator.ts b/apps/emails-and-messages/src/modules/app-configuration/app-configurator.ts deleted file mode 100644 index 18a1b2d..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/app-configurator.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { AppConfig } from "./app-config"; -import { SettingsManager } from "@saleor/app-sdk/settings-manager"; - -export interface AppConfigurator { - setConfig(config: AppConfig): Promise; - getConfig(): Promise; -} - -export class PrivateMetadataAppConfigurator implements AppConfigurator { - private metadataKey = "app-config"; - - constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {} - - getConfig(): Promise { - return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => { - if (!data) { - return data; - } - - try { - return JSON.parse(data); - } catch (e) { - throw new Error("Invalid metadata value, cant be parsed"); - } - }); - } - - setConfig(config: AppConfig): Promise { - return this.metadataManager.set({ - key: this.metadataKey, - value: JSON.stringify(config), - domain: this.saleorApiUrl, - }); - } -} diff --git a/apps/emails-and-messages/src/modules/app-configuration/fallback-app-config.ts b/apps/emails-and-messages/src/modules/app-configuration/fallback-app-config.ts deleted file mode 100644 index d1d9651..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/fallback-app-config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { AppConfig } from "./app-config"; -import { AppConfigContainer, getDefaultEmptyAppConfiguration } from "./app-config-container"; -import { ChannelFragment, ShopInfoFragment } from "../../../generated/graphql"; - -/** - * TODO Test - */ -export const FallbackAppConfig = { - createFallbackConfigFromExistingShopAndChannels( - channels: ChannelFragment[], - shopAppConfiguration: ShopInfoFragment | null - ) { - return (channels ?? []).reduce( - (state, channel) => { - return AppConfigContainer.setChannelAppConfiguration(state)(channel.slug)( - getDefaultEmptyAppConfiguration() - ); - }, - { configurationsPerChannel: {} } - ); - }, -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts b/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts deleted file mode 100644 index c02fd90..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { PrivateMetadataAppConfigurator } from "./app-configurator"; -import { Client } from "urql"; -import { createLogger } from "@saleor/apps-shared"; -import { AppConfig, AppConfigurationPerChannel } from "./app-config"; -import { getDefaultEmptyAppConfiguration } from "./app-config-container"; -import { createSettingsManager } from "../../lib/metadata-manager"; - -const logger = createLogger({ - service: "AppConfigurationService", -}); - -export class AppConfigurationService { - private configurationData?: AppConfig; - private metadataConfigurator: PrivateMetadataAppConfigurator; - - constructor(args: { apiClient: Client; saleorApiUrl: string; initialData?: AppConfig }) { - this.metadataConfigurator = new PrivateMetadataAppConfigurator( - createSettingsManager(args.apiClient), - args.saleorApiUrl - ); - } - - // Fetch configuration from Saleor API and cache it - private async pullConfiguration() { - logger.debug("Fetch configuration from Saleor API"); - - const config = await this.metadataConfigurator.getConfig(); - - this.configurationData = config; - } - - // Push configuration to Saleor API - private async pushConfiguration() { - logger.debug("Push configuration to Saleor API"); - - await this.metadataConfigurator.setConfig(this.configurationData!); - } - - async getConfiguration() { - logger.debug("Get configuration"); - - if (!this.configurationData) { - logger.debug("No configuration found in cache. Will fetch it from Saleor API"); - await this.pullConfiguration(); - } - - const savedAppConfig = this.configurationData ?? null; - - logger.debug(savedAppConfig, "Retrieved app config from Metadata. Will return it"); - - if (savedAppConfig) { - return savedAppConfig; - } - } - - // Saves configuration to Saleor API and cache it - async setConfigurationRoot(config: AppConfig) { - logger.debug("Set configuration"); - - this.configurationData = config; - await this.pushConfiguration(); - } - - // Returns channel configuration if existing. Otherwise returns default empty one - async getChannelConfiguration(channel: string) { - logger.debug("Get channel configuration"); - const configurations = await this.getConfiguration(); - - if (!configurations) { - return getDefaultEmptyAppConfiguration(); - } - - const channelConfiguration = configurations.configurationsPerChannel[channel]; - - return channelConfiguration || getDefaultEmptyAppConfiguration(); - } - - async setChannelConfiguration({ - channel, - configuration, - }: { - channel: string; - configuration: AppConfigurationPerChannel; - }) { - logger.debug("Set channel configuration"); - let configurations = await this.getConfiguration(); - - if (!configurations) { - configurations = { configurationsPerChannel: {} }; - } - - configurations.configurationsPerChannel[channel] = configuration; - await this.setConfigurationRoot(configurations); - } -} diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx deleted file mode 100644 index 5708779..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { AppConfigurationPerChannel } from "../app-config"; -import { Controller, useForm } from "react-hook-form"; -import { FormControl, InputLabel, Link, MenuItem, Select, Typography } from "@material-ui/core"; -import { Button, makeStyles, SwitchSelector, SwitchSelectorButton } from "@saleor/macaw-ui"; -import React, { useEffect } from "react"; -import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { useRouter } from "next/router"; -import { mjmlUrls } from "../../mjml/urls"; - -const useStyles = makeStyles((theme) => ({ - field: { - marginBottom: 20, - }, - channelName: { - cursor: "pointer", - borderBottom: `2px solid ${theme.palette.secondary.main}`, - }, -})); - -type AppConfigurationFormProps = { - channelSlug: string; - channelName: string; - channelID: string; - mjmlConfigurationChoices: { label: string; value: string }[]; - sendgridConfigurationChoices: { label: string; value: string }[]; - onSubmit(data: AppConfigurationPerChannel): Promise; - initialData?: AppConfigurationPerChannel | null; -}; - -export const AppConfigurationForm = (props: AppConfigurationFormProps) => { - const styles = useStyles(); - const { appBridge } = useAppBridge(); - const router = useRouter(); - - const { handleSubmit, getValues, setValue, control, reset } = useForm( - { - defaultValues: props.initialData ?? undefined, - } - ); - - useEffect(() => { - reset(props.initialData || undefined); - }, [props.initialData, reset]); - - const handleChannelNameClick = () => { - appBridge?.dispatch( - actions.Redirect({ - to: `/channels/${props.channelID}`, - }) - ); - }; - - const isNoSendgridConfigurations = !props.sendgridConfigurationChoices.length; - const isNoMjmlConfigurations = !props.mjmlConfigurationChoices.length; - - return ( -
{ - props.onSubmit(data); - })} - > - - Configure - - {` ${props.channelName} `} - - channel: - - - ( -
- {/* 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() || "false"} - key={button.label} - > - {button.label} - - ))} - -
- )} - /> - - { - return ( - - MJML Configuration - - {isNoMjmlConfigurations && ( - { - router.push(mjmlUrls.configuration()); - }} - > - - Currently theres no MJML configuration available. Click here to create one. - - - )} - - ); - }} - /> - - { - return ( - - Sendgrid Configuration - - {isNoSendgridConfigurations && ( - { - router.push(""); - }} - > - - Currently theres no Sendgrid configuration available. Click here to create one. - - - )} - - ); - }} - /> - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx deleted file mode 100644 index 51130ac..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import React, { useMemo, useState } from "react"; -import { EditIcon, IconButton, makeStyles } from "@saleor/macaw-ui"; -import { AppConfigurationForm } from "./app-configuration-form"; -import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { AppColumnsLayout } from "../../ui/app-columns-layout"; -import { trpcClient } from "../../trpc/trpc-client"; -import { SideMenu } from "./side-menu"; -import { LoadingIndicator } from "../../ui/loading-indicator"; -import { Instructions } from "./instructions"; -import { useDashboardNotification } from "@saleor/apps-shared"; - -const useStyles = makeStyles((theme) => { - return { - formContainer: { - top: 0, - }, - configurationColumn: { - display: "flex", - flexDirection: "column", - gap: 20, - maxWidth: 700, - }, - }; -}); - -export const ChannelsConfigurationTab = () => { - const styles = useStyles(); - const { appBridge } = useAppBridge(); - const [activeChannelSlug, setActiveChannelSlug] = useState(null); - const { notifySuccess } = useDashboardNotification(); - - const { data: channelsData, isLoading: isChannelsDataLoading } = - trpcClient.channels.fetch.useQuery(undefined, { - onSuccess: (data) => { - if (data?.length) { - setActiveChannelSlug(data[0].slug); - } - }, - }); - - const { - data: configurationData, - refetch: refetchConfig, - isLoading: isConfigurationDataLoading, - } = trpcClient.appConfiguration.getChannelConfiguration.useQuery( - { - channelSlug: activeChannelSlug!, - }, - { enabled: !!activeChannelSlug } - ); - - const { data: mjmlConfigurations, isLoading: isMjmlQueryLoading } = - trpcClient.mjmlConfiguration.getConfigurations.useQuery({}); - - const mjmlConfigurationsListData = useMemo(() => { - return ( - mjmlConfigurations?.map((configuration) => ({ - value: configuration.id, - label: configuration.configurationName, - })) ?? [] - ); - }, [mjmlConfigurations]); - - const { data: sendgridConfigurations, isLoading: isSendgridQueryLoading } = - trpcClient.sendgridConfiguration.getConfigurations.useQuery({}); - - const sendgridConfigurationsListData = useMemo(() => { - return ( - sendgridConfigurations?.map((configuration) => ({ - value: configuration.id, - label: configuration.configurationName, - })) ?? [] - ); - }, [sendgridConfigurations]); - - const { mutate: mutateAppChannelConfiguration, error: saveError } = - trpcClient.appConfiguration.setChannelConfiguration.useMutation({ - onSuccess() { - refetchConfig(); - - notifySuccess("Success", "Saved app configuration"); - }, - }); - - const activeChannel = channelsData?.find((c) => c.slug === activeChannelSlug); - - if (isChannelsDataLoading) { - return ; - } - if (!channelsData?.length) { - return
NO CHANNELS
; - } - - const isFormDataLoading = - isConfigurationDataLoading || isMjmlQueryLoading || isSendgridQueryLoading; - - return ( - - { - appBridge?.dispatch( - actions.Redirect({ - to: `/channels/`, - }) - ); - }} - > - - - } - onClick={(id) => setActiveChannelSlug(id)} - items={channelsData.map((c) => ({ label: c.name, id: c.slug })) || []} - /> -
- {!activeChannel || isFormDataLoading ? ( - - ) : ( - <> - { - mutateAppChannelConfiguration({ - channel: activeChannel.slug, - configuration: data, - }); - }} - initialData={configurationData} - channelName={activeChannel?.name ?? activeChannelSlug} - /> - {saveError && {saveError.message}} - - )} -
- -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-list.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/channels-list.tsx deleted file mode 100644 index 35a8e3e..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-list.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { - makeStyles, - OffsettedList, - OffsettedListBody, - OffsettedListHeader, - OffsettedListItem, - OffsettedListItemCell, -} from "@saleor/macaw-ui"; -import clsx from "clsx"; -import { Typography } from "@material-ui/core"; -import React from "react"; -import { ChannelFragment } from "../../../../generated/graphql"; - -const useStyles = makeStyles((theme) => { - return { - listItem: { - cursor: "pointer", - height: "auto !important", - }, - listItemActive: { - background: "#f4f4f4", - borderRadius: 4, - overflow: "hidden", - }, - channelSlug: { - fontFamily: "monospace", - opacity: 0.8, - }, - }; -}); - -type Props = { - channels: ChannelFragment[]; - activeChannelSlug: string; - onChannelClick(channelSlug: string): void; -}; - -export const ChannelsList = ({ channels, activeChannelSlug, onChannelClick }: Props) => { - const styles = useStyles(); - - return ( - - - - Available channels - - - - {channels.map((c) => { - return ( - { - onChannelClick(c.slug); - }} - > - - {c.name} - - {c.slug} - - - - ); - })} - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/configurations-list.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/configurations-list.tsx deleted file mode 100644 index 0cba3b4..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/configurations-list.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { - DeleteIcon, - IconButton, - makeStyles, - OffsettedList, - OffsettedListBody, - OffsettedListItem, - OffsettedListItemCell, -} from "@saleor/macaw-ui"; -import clsx from "clsx"; -import React from "react"; - -const useStyles = makeStyles((theme) => { - return { - listItem: { - cursor: "pointer", - height: "auto !important", - }, - listItemActive: { - background: "#f4f4f4", - borderRadius: 4, - overflow: "hidden", - }, - channelSlug: { - fontFamily: "monospace", - opacity: 0.8, - }, - }; -}); - -type ListItem = { - label: string; - id: string; -}; - -type Props = { - listItems: ListItem[]; - activeItemId?: string; - onItemClick(itemId?: string): void; -}; - -export const ConfigurationsList = ({ listItems, activeItemId, onItemClick }: Props) => { - const styles = useStyles(); - return ( - - - {listItems.map((c) => { - return ( - { - onItemClick(c.id); - }} - > - - {c.label} - { - event.stopPropagation(); - event.preventDefault(); - }} - > - - - - - ); - })} - { - onItemClick(); - }} - > - Create new - - - - ); -}; diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/side-menu.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/side-menu.tsx deleted file mode 100644 index 16578b2..0000000 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/side-menu.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { Card, CardContent, CardHeader, Divider } from "@material-ui/core"; -("@material-ui/icons"); -import { DeleteIcon, IconButton, List, ListItem, ListItemCell } from "@saleor/macaw-ui"; -import clsx from "clsx"; -import React from "react"; - -import { makeStyles } from "@saleor/macaw-ui"; -import { Skeleton } from "@material-ui/lab"; - -export const useStyles = makeStyles((theme) => ({ - menu: { - height: "fit-content", - }, - clickable: { - cursor: "pointer", - }, - selected: { - "&&&&::before": { - position: "absolute", - left: 0, - width: "4px", - height: "100%", - backgroundColor: theme.palette.saleor.active[1], - }, - }, - spaceBetween: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - }, - tableRow: { - minHeight: "48px", - "&::after": { - display: "none", - }, - }, - greyText: { - color: theme.palette.text.hint, - }, - link: { - all: "inherit", - display: "contents", - }, -})); - -interface SideMenuProps { - title: string; - noItemsText?: string; - items: { id: string; label: string }[]; - selectedItemId?: string; - headerToolbar?: React.ReactNode; - onDelete?: (itemId: string) => void; - onClick: (itemId: string) => void; -} - -export const SideMenu: React.FC = ({ - title, - items, - headerToolbar, - selectedItemId, - noItemsText, - onDelete, - onClick, -}) => { - const classes = useStyles(); - - const isNoItems = !items || !items.length; - return ( - - - {isNoItems ? ( - !!noItemsText && {noItemsText} - ) : ( - - {items.map((item) => ( - - - onClick(item.id)} - > - -
- {item.label} - {!!onDelete && ( - { - event.stopPropagation(); - event.preventDefault(); - onDelete(item.id); - }} - > - - - )} -
-
-
-
- )) ?? } - -
- )} -
- ); -}; diff --git a/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts b/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts index e45fe37..f6487c9 100644 --- a/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts +++ b/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts @@ -1,7 +1,6 @@ import { AuthData } from "@saleor/app-sdk/APL"; import { Client } from "urql"; import { createLogger } from "@saleor/apps-shared"; -import { AppConfigurationService } from "../app-configuration/get-app-configuration.service"; import { MjmlConfigurationService } from "../mjml/configuration/get-mjml-configuration.service"; import { sendMjml } from "../mjml/send-mjml"; import { SendgridConfigurationService } from "../sendgrid/configuration/get-sendgrid-configuration.service"; @@ -31,75 +30,53 @@ export const sendEventMessages = async ({ logger.debug("Function called"); - const appConfigurationService = new AppConfigurationService({ + const mjmlConfigurationService = new MjmlConfigurationService({ apiClient: client, saleorApiUrl: authData.saleorApiUrl, }); - const channelAppConfiguration = await appConfigurationService.getChannelConfiguration(channel); + const availableMjmlConfigurations = await mjmlConfigurationService.getConfigurations({ + active: true, + availableInChannel: channel, + }); - if (!channelAppConfiguration) { - logger.warn("App has no configuration for this channel"); - return; - } - logger.debug("Channel has assigned app configuration"); - - if (!channelAppConfiguration.active) { - logger.warn("App configuration is not active for this channel"); - return; - } - - if (channelAppConfiguration.mjmlConfigurationId) { - logger.debug("Channel has assigned MJML configuration"); - - const mjmlConfigurationService = new MjmlConfigurationService({ - apiClient: client, - saleorApiUrl: authData.saleorApiUrl, + for (const mjmlConfiguration of availableMjmlConfigurations) { + const mjmlStatus = await sendMjml({ + event, + payload, + recipientEmail, + mjmlConfiguration, }); - const mjmlConfiguration = await mjmlConfigurationService.getConfiguration({ - id: channelAppConfiguration.mjmlConfigurationId, - }); - - if (mjmlConfiguration) { - const mjmlStatus = await sendMjml({ - event, - payload, - recipientEmail, - mjmlConfiguration, - }); - - if (mjmlStatus?.errors.length) { - logger.error("MJML errors"); - logger.error(mjmlStatus?.errors); - } + if (mjmlStatus?.errors.length) { + logger.error("MJML errors"); + logger.error(mjmlStatus?.errors); } } - if (channelAppConfiguration.sendgridConfigurationId) { - logger.debug("Channel has assigned Sendgrid configuration"); + logger.debug("Channel has assigned Sendgrid configuration"); - const sendgridConfigurationService = new SendgridConfigurationService({ - apiClient: client, - saleorApiUrl: authData.saleorApiUrl, + const sendgridConfigurationService = new SendgridConfigurationService({ + apiClient: client, + saleorApiUrl: authData.saleorApiUrl, + }); + + const availableSendgridConfigurations = await sendgridConfigurationService.getConfigurations({ + active: true, + availableInChannel: channel, + }); + + for (const sendgridConfiguration of availableSendgridConfigurations) { + const sendgridStatus = await sendSendgrid({ + event, + payload, + recipientEmail, + sendgridConfiguration, }); - const sendgridConfiguration = await sendgridConfigurationService.getConfiguration({ - id: channelAppConfiguration.sendgridConfigurationId, - }); - - if (sendgridConfiguration) { - const sendgridStatus = await sendSendgrid({ - event, - payload, - recipientEmail, - sendgridConfiguration, - }); - - if (sendgridStatus?.errors.length) { - logger.error("Sendgrid errors"); - logger.error(sendgridStatus?.errors); - } + if (sendgridStatus?.errors.length) { + logger.error("Sendgrid errors"); + logger.error(sendgridStatus?.errors); } } }; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts index c42bb83..c6d4606 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts @@ -2,6 +2,7 @@ import { messageEventTypes } from "../../event-handlers/message-event-types"; import { MjmlConfig as MjmlConfigurationRoot, MjmlConfiguration } from "./mjml-config"; import { defaultMjmlTemplates, defaultMjmlSubjectTemplates } from "../default-templates"; import { generateRandomId } from "../../../lib/generate-random-id"; +import { isAvailableInChannel } from "../../../lib/is-available-in-channel"; export const getDefaultEventsConfiguration = (): MjmlConfiguration["events"] => messageEventTypes.map((eventType) => ({ @@ -24,6 +25,10 @@ export const getDefaultEmptyConfiguration = (): MjmlConfiguration => { smtpPassword: "", encryption: "NONE", events: getDefaultEventsConfiguration(), + channels: { + excludedFrom: [], + restrictedTo: [], + }, }; return defaultConfig; @@ -45,6 +50,7 @@ const getConfiguration = export interface FilterConfigurationsArgs { ids?: string[]; + availableInChannel?: string; active?: boolean; } @@ -57,14 +63,28 @@ const getConfigurations = let filtered = mjmlConfigRoot.configurations; - if (filter?.ids?.length) { + if (!filter) { + return filtered; + } + + if (filter.ids?.length) { filtered = filtered.filter((c) => filter?.ids?.includes(c.id)); } - if (filter?.active !== undefined) { + if (filter.active !== undefined) { filtered = filtered.filter((c) => c.active === filter.active); } + if (filter.availableInChannel?.length) { + filtered = filtered.filter((c) => + isAvailableInChannel({ + channel: filter.availableInChannel!, + restrictedToChannels: c.channels.restrictedTo, + excludedChannels: c.channels.excludedFrom, + }) + ); + } + return filtered; }; @@ -79,6 +99,7 @@ const createConfiguration = id: generateRandomId(), events: getDefaultEventsConfiguration(), }; + mjmlConfigNormalized.configurations.push(newConfiguration); return mjmlConfigNormalized; }; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-input-schema.ts b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-input-schema.ts index 88ce896..19ad5f5 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-input-schema.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-input-schema.ts @@ -19,6 +19,10 @@ export const mjmlConfigurationBaseObjectSchema = z.object({ smtpUser: z.string(), smtpPassword: z.string(), encryption: z.enum(smtpEncryptionTypes), + channels: z.object({ + excludedFrom: z.array(z.string()), + restrictedTo: z.array(z.string()), + }), }); export const mjmlCreateConfigurationSchema = mjmlConfigurationBaseObjectSchema; diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config.ts b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config.ts index 93fccb7..b4b59bc 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config.ts @@ -15,6 +15,10 @@ export interface MjmlConfiguration { id: string; active: boolean; configurationName: string; + channels: { + excludedFrom: string[]; + restrictedTo: string[]; + }; senderName: string; senderEmail: string; smtpHost: string; 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 index d535916..0ceef17 100644 --- 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 @@ -1,5 +1,5 @@ import React from "react"; -import { IconButton, makeStyles } from "@saleor/macaw-ui"; +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"; @@ -7,11 +7,8 @@ import { getDefaultEmptyConfiguration } from "../mjml-config-container"; import { NextRouter, useRouter } from "next/router"; import { mjmlUrls } from "../../urls"; import { MjmlTemplatesCard } from "./mjml-templates-card"; -import { SideMenu } from "../../../app-configuration/ui/side-menu"; import { MjmlConfiguration } from "../mjml-config"; import { LoadingIndicator } from "../../../ui/loading-indicator"; -import { Add } from "@material-ui/icons"; -import { useQueryClient } from "@tanstack/react-query"; import { MjmlInstructions } from "./mjml-instructions"; import { useDashboardNotification } from "@saleor/apps-shared"; @@ -42,6 +39,7 @@ const navigateToFirstConfiguration = (router: NextRouter, configurations?: MjmlC return; } const firstConfigurationId = configurations[0]?.id; + if (firstConfigurationId) { router.replace(mjmlUrls.configuration(firstConfigurationId)); return; @@ -50,16 +48,13 @@ const navigateToFirstConfiguration = (router: NextRouter, configurations?: MjmlC export const MjmlConfigurationTab = ({ configurationId }: MjmlConfigurationTabProps) => { const styles = useStyles(); - const { notifyError, notifySuccess } = useDashboardNotification(); const router = useRouter(); - const queryClient = useQueryClient(); const { data: configurations, refetch: refetchConfigurations, isLoading: configurationsIsLoading, isFetching: configurationsIsFetching, - isRefetching: configurationsIsRefetching, } = trpcClient.mjmlConfiguration.getConfigurations.useQuery(undefined, { onSuccess(data) { if (!configurationId) { @@ -69,38 +64,6 @@ export const MjmlConfigurationTab = ({ configurationId }: MjmlConfigurationTabPr }, }); - const { mutate: deleteConfiguration } = - trpcClient.mjmlConfiguration.deleteConfiguration.useMutation({ - onError: (error) => { - notifyError("Could not remove the configuration", error.message); - }, - onSuccess: async (_data, variables) => { - await queryClient.cancelQueries({ queryKey: ["mjmlConfiguration", "getConfigurations"] }); - // remove value from the cache after the success - queryClient.setQueryData>( - ["mjmlConfiguration", "getConfigurations"], - (old) => { - if (old) { - const index = old.findIndex((c) => c.id === variables.id); - if (index !== -1) { - delete old[index]; - return [...old]; - } - } - } - ); - - // if we just deleted the configuration that was selected - // we have to update the URL - if (variables.id === configurationId) { - router.replace(mjmlUrls.configuration()); - } - - refetchConfigurations(); - notifySuccess("Success", "Removed successfully"); - }, - }); - if (configurationsIsLoading || configurationsIsFetching) { return ; } @@ -113,25 +76,6 @@ export const MjmlConfigurationTab = ({ configurationId }: MjmlConfigurationTabPr return ( - { - router.replace(mjmlUrls.configuration()); - }} - > - - - } - onClick={(id) => router.replace(mjmlUrls.configuration(id))} - onDelete={(id) => { - deleteConfiguration({ id }); - }} - items={configurations?.map((c) => ({ label: c.configurationName, id: c.id })) || []} - />
{configurationsIsLoading || configurationsIsFetching ? ( diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts index cd91782..a649981 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-container.ts @@ -1,4 +1,5 @@ import { generateRandomId } from "../../../lib/generate-random-id"; +import { isAvailableInChannel } from "../../../lib/is-available-in-channel"; import { messageEventTypes } from "../../event-handlers/message-event-types"; import { SendgridConfig as SendgridConfigurationRoot, @@ -22,6 +23,10 @@ export const getDefaultEmptyConfiguration = (): SendgridConfiguration => { apiKey: "", sandboxMode: false, events: getDefaultEventsConfiguration(), + channels: { + excludedFrom: [], + restrictedTo: [], + }, }; return defaultConfig; @@ -44,6 +49,7 @@ const getConfiguration = export interface FilterConfigurationsArgs { ids?: string[]; active?: boolean; + availableInChannel?: string; } const getConfigurations = @@ -55,14 +61,28 @@ const getConfigurations = let filtered = sendgridConfigRoot.configurations; - if (filter?.ids?.length) { + if (!filter) { + return filtered; + } + + if (filter.ids?.length) { filtered = filtered.filter((c) => filter?.ids?.includes(c.id)); } - if (filter?.active !== undefined) { + if (filter.active !== undefined) { filtered = filtered.filter((c) => c.active === filter.active); } + if (filter.availableInChannel?.length) { + filtered = filtered.filter((c) => + isAvailableInChannel({ + channel: filter.availableInChannel!, + restrictedToChannels: c.channels.restrictedTo, + excludedChannels: c.channels.excludedFrom, + }) + ); + } + return filtered; }; @@ -77,6 +97,7 @@ const createConfiguration = id: generateRandomId(), events: getDefaultEventsConfiguration(), }; + sendgridConfigNormalized.configurations.push(newConfiguration); return sendgridConfigNormalized; }; 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 70e76bd..b6dd299 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 @@ -14,6 +14,10 @@ export const sendgridConfigurationBaseObjectSchema = z.object({ apiKey: z.string().min(1), senderName: z.string().min(1).optional(), senderEmail: z.string().email().min(5).optional(), + channels: z.object({ + excludedFrom: z.array(z.string()), + restrictedTo: z.array(z.string()), + }), }); export const sendgridCreateConfigurationSchema = sendgridConfigurationBaseObjectSchema.omit({ diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts index f7bfb33..d231e30 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config.ts @@ -15,6 +15,10 @@ export interface SendgridConfiguration { senderEmail?: string; apiKey: string; events: SendgridEventConfiguration[]; + channels: { + excludedFrom: string[]; + restrictedTo: string[]; + }; } export type SendgridConfig = { 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 index a8c6367..c78e1c4 100644 --- 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 @@ -1,19 +1,15 @@ import React from "react"; -import { IconButton, makeStyles } from "@saleor/macaw-ui"; +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 { SideMenu } from "../../../app-configuration/ui/side-menu"; import { SendgridConfiguration } from "../sendgrid-config"; import { LoadingIndicator } from "../../../ui/loading-indicator"; -import { Add } from "@material-ui/icons"; -import { useQueryClient } from "@tanstack/react-query"; import { sendgridUrls } from "../../urls"; import { SendgridTemplatesCard } from "./sendgrid-templates-card"; import { SendgridInstructions } from "./sendgrid-instructions"; -import { useDashboardNotification } from "@saleor/apps-shared"; const useStyles = makeStyles((theme) => { return { @@ -45,6 +41,7 @@ const navigateToFirstConfiguration = ( return; } const firstConfigurationId = configurations[0]?.id; + if (firstConfigurationId) { router.replace(sendgridUrls.configuration(firstConfigurationId)); return; @@ -54,15 +51,12 @@ const navigateToFirstConfiguration = ( export const SendgridConfigurationTab = ({ configurationId }: SendgridConfigurationTabProps) => { const styles = useStyles(); const router = useRouter(); - const queryClient = useQueryClient(); - const { notifySuccess, notifyError } = useDashboardNotification(); const { data: configurations, refetch: refetchConfigurations, isLoading: configurationsIsLoading, isFetching: configurationsIsFetching, - isRefetching: configurationsIsRefetching, } = trpcClient.sendgridConfiguration.getConfigurations.useQuery(undefined, { onSuccess(data) { if (!configurationId) { @@ -72,41 +66,6 @@ export const SendgridConfigurationTab = ({ configurationId }: SendgridConfigurat }, }); - const { mutate: deleteConfiguration } = - trpcClient.sendgridConfiguration.deleteConfiguration.useMutation({ - onError: (error) => { - notifyError("Could not remove the configuration", error.message); - }, - onSuccess: async (_data, variables) => { - await queryClient.cancelQueries({ - queryKey: ["sendgridConfiguration", "getConfigurations"], - }); - // remove value from the cache after the success - queryClient.setQueryData>( - ["sendgridConfiguration", "getConfigurations"], - (old) => { - if (old) { - const index = old.findIndex((c) => c.id === variables.id); - if (index !== -1) { - delete old[index]; - return [...old]; - } - } - } - ); - - // if we just deleted the configuration that was selected - // we have to update the URL - if (variables.id === configurationId) { - router.replace(sendgridUrls.configuration()); - } - - refetchConfigurations(); - - notifySuccess("Success", "Removed successfully"); - }, - }); - if (configurationsIsLoading || configurationsIsFetching) { return ; } @@ -119,25 +78,6 @@ export const SendgridConfigurationTab = ({ configurationId }: SendgridConfigurat return ( - { - router.replace(sendgridUrls.configuration()); - }} - > - - - } - onClick={(id) => router.replace(sendgridUrls.configuration(id))} - onDelete={(id) => { - deleteConfiguration({ id }); - }} - items={configurations?.map((c) => ({ label: c.configurationName, id: c.id })) || []} - />
{configurationsIsLoading || configurationsIsFetching ? ( diff --git a/apps/emails-and-messages/src/modules/trpc/trpc-app-router.ts b/apps/emails-and-messages/src/modules/trpc/trpc-app-router.ts index a29bbf2..67d020f 100644 --- a/apps/emails-and-messages/src/modules/trpc/trpc-app-router.ts +++ b/apps/emails-and-messages/src/modules/trpc/trpc-app-router.ts @@ -1,12 +1,10 @@ import { channelsRouter } from "../channels/channels.router"; import { router } from "./trpc-server"; -import { appConfigurationRouter } from "../app-configuration/app-configuration.router"; import { mjmlConfigurationRouter } from "../mjml/configuration/mjml-configuration.router"; import { sendgridConfigurationRouter } from "../sendgrid/configuration/sendgrid-configuration.router"; export const appRouter = router({ channels: channelsRouter, - appConfiguration: appConfigurationRouter, mjmlConfiguration: mjmlConfigurationRouter, sendgridConfiguration: sendgridConfigurationRouter, }); 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 index e4ad9d8..ca26174 100644 --- a/apps/emails-and-messages/src/modules/ui/app-columns-layout.tsx +++ b/apps/emails-and-messages/src/modules/ui/app-columns-layout.tsx @@ -4,7 +4,7 @@ import { PropsWithChildren } from "react"; const useStyles = makeStyles((theme) => ({ root: { display: "grid", - gridTemplateColumns: "280px auto 400px", + gridTemplateColumns: "auto 400px", alignItems: "start", gap: theme.spacing(3), padding: "20px 0", diff --git a/apps/emails-and-messages/src/pages/configuration/channels/index.tsx b/apps/emails-and-messages/src/pages/configuration/channels/index.tsx index 23e85b0..adbcf77 100644 --- a/apps/emails-and-messages/src/pages/configuration/channels/index.tsx +++ b/apps/emails-and-messages/src/pages/configuration/channels/index.tsx @@ -3,12 +3,14 @@ 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"; -import { ChannelsConfigurationTab } from "../../../modules/app-configuration/ui/channels-configuration-tab"; 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"); @@ -16,7 +18,18 @@ const ChannelsConfigurationPage: NextPage = () => { }, [channels.data, channels.isSuccess, router]); return ( - + Sendgrid configurations: +
    + {sendgridConfigurations.data?.map((c) => ( +
  • {c.configurationName}
  • + ))} +
+ MJML configurations: +
    + {mjmlConfigurations.data?.map((c) => ( +
  • {c.configurationName}
  • + ))} +
); }; diff --git a/apps/products-feed/graphql/queries/FetchProductDataForFeed.graphql b/apps/products-feed/graphql/queries/FetchProductDataForFeed.graphql index a336ba0..d23be3a 100644 --- a/apps/products-feed/graphql/queries/FetchProductDataForFeed.graphql +++ b/apps/products-feed/graphql/queries/FetchProductDataForFeed.graphql @@ -1,4 +1,4 @@ -query FetchProductDataForFeed($first:Int!, $after: String, $channel: String!){ +query FetchProductDataForFeed($first:Int!, $after: String, $channel: String!, $language: String!){ productVariants(first:$first, after: $after, channel: $channel){ pageInfo{ hasNextPage