From 37e50db29c11b6e935e36c281e1e9a4b3f27e31e Mon Sep 17 00:00:00 2001 From: Lukasz Ostrowski Date: Wed, 28 Jun 2023 09:45:35 +0200 Subject: [PATCH] update macaw in klaviyo (#670) * update macaw in klaviyo * cr fixes * removed e2e artifacts --- .changeset/proud-phones-change.md | 5 + apps/klaviyo/next.config.js | 9 -- apps/klaviyo/package.json | 3 +- .../AccessWarning/AccessWarning.tsx | 32 ----- .../components/LoadingPage/LoadingPage.tsx | 18 --- .../src/components/LoadingPage/styles.ts | 14 -- apps/klaviyo/src/hooks/theme-synchronizer.tsx | 32 ++--- .../klaviyo/src/lib/ui/app-columns-layout.tsx | 32 ++--- apps/klaviyo/src/pages/_app.tsx | 94 ++------------ apps/klaviyo/src/pages/configuration.tsx | 120 +++++++----------- apps/klaviyo/src/pages/index.tsx | 32 +++++ pnpm-lock.yaml | 7 +- 12 files changed, 131 insertions(+), 267 deletions(-) create mode 100644 .changeset/proud-phones-change.md delete mode 100644 apps/klaviyo/src/components/AccessWarning/AccessWarning.tsx delete mode 100644 apps/klaviyo/src/components/LoadingPage/LoadingPage.tsx delete mode 100644 apps/klaviyo/src/components/LoadingPage/styles.ts create mode 100644 apps/klaviyo/src/pages/index.tsx diff --git a/.changeset/proud-phones-change.md b/.changeset/proud-phones-change.md new file mode 100644 index 0000000..bea685f --- /dev/null +++ b/.changeset/proud-phones-change.md @@ -0,0 +1,5 @@ +--- +"saleor-app-klaviyo": minor +--- + +Rewritten app to use @saleor/macaw-ui/next. App should work faster and be visually more aligned with rest of the Dashboard. diff --git a/apps/klaviyo/next.config.js b/apps/klaviyo/next.config.js index 51c72b4..6c8dd36 100644 --- a/apps/klaviyo/next.config.js +++ b/apps/klaviyo/next.config.js @@ -7,15 +7,6 @@ const isSentryPropertiesInEnvironment = const nextConfig = { reactStrictMode: true, transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw"], - redirects() { - return [ - { - source: "/", - destination: "/configuration", - permanent: false, - }, - ]; - }, }; const configWithSentry = withSentryConfig( diff --git a/apps/klaviyo/package.json b/apps/klaviyo/package.json index 1f4a18b..4eb84be 100644 --- a/apps/klaviyo/package.json +++ b/apps/klaviyo/package.json @@ -16,7 +16,7 @@ "@material-ui/lab": "4.0.0-alpha.61", "@saleor/app-sdk": "0.40.1", "@saleor/apps-shared": "workspace:*", - "@saleor/macaw-ui": "^0.7.2", + "@saleor/macaw-ui": "0.8.0-pre.95", "@sentry/nextjs": "7.55.2", "@urql/exchange-auth": "^2.1.4", "clsx": "^1.2.1", @@ -30,6 +30,7 @@ "react-dom": "18.2.0", "react-helmet": "^6.1.0", "urql": "^4.0.4", + "usehooks-ts": "^2.9.1", "vite": "4.3.9", "vitest": "0.31.3" }, diff --git a/apps/klaviyo/src/components/AccessWarning/AccessWarning.tsx b/apps/klaviyo/src/components/AccessWarning/AccessWarning.tsx deleted file mode 100644 index 1091756..0000000 --- a/apps/klaviyo/src/components/AccessWarning/AccessWarning.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Typography } from "@material-ui/core"; -import React from "react"; - -type WarningCause = - | "not_in_iframe" - | "missing_access_token" - | "invalid_access_token" - | "unknown_cause"; - -interface AccessWarningProps { - cause?: WarningCause; -} - -const warnings: Record = { - not_in_iframe: "The view can only be displayed in the iframe.", - missing_access_token: "App doesn't have an access token.", - invalid_access_token: "Access token is invalid.", - unknown_cause: "Something went wrong.", -}; - -export function AccessWarning({ cause = "unknown_cause" }: AccessWarningProps) { - return ( -
- - App can't be accessed outside of the Saleor Dashboard - - - ❌ {warnings[cause]} - -
- ); -} diff --git a/apps/klaviyo/src/components/LoadingPage/LoadingPage.tsx b/apps/klaviyo/src/components/LoadingPage/LoadingPage.tsx deleted file mode 100644 index 69c2501..0000000 --- a/apps/klaviyo/src/components/LoadingPage/LoadingPage.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { CircularProgress, Typography } from "@material-ui/core"; -import React from "react"; - -import { useStyles } from "./styles"; - -export function LoadingPage() { - const classes = useStyles(); - - return ( -
- - - - Attempting connection to Saleor Dashboard - -
- ); -} diff --git a/apps/klaviyo/src/components/LoadingPage/styles.ts b/apps/klaviyo/src/components/LoadingPage/styles.ts deleted file mode 100644 index b392343..0000000 --- a/apps/klaviyo/src/components/LoadingPage/styles.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles((theme) => ({ - loaderContainer: { - display: "flex", - flexDirection: "column", - alignItems: "center", - }, - message: { - marginTop: theme.spacing(4), - }, -})); - -export { useStyles }; diff --git a/apps/klaviyo/src/hooks/theme-synchronizer.tsx b/apps/klaviyo/src/hooks/theme-synchronizer.tsx index 7e5ce17..3386f49 100644 --- a/apps/klaviyo/src/hooks/theme-synchronizer.tsx +++ b/apps/klaviyo/src/hooks/theme-synchronizer.tsx @@ -1,33 +1,25 @@ 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() { +// todo move to shared +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/klaviyo/src/lib/ui/app-columns-layout.tsx b/apps/klaviyo/src/lib/ui/app-columns-layout.tsx index 167cced..9e1ae6d 100644 --- a/apps/klaviyo/src/lib/ui/app-columns-layout.tsx +++ b/apps/klaviyo/src/lib/ui/app-columns-layout.tsx @@ -1,21 +1,17 @@ -import { makeStyles } from "@saleor/macaw-ui"; +import { Box } from "@saleor/macaw-ui/next"; import { PropsWithChildren } from "react"; -const useStyles = makeStyles({ - root: { - display: "grid", - gridTemplateColumns: "280px auto 280px", - alignItems: "start", - gap: 32, - maxWidth: 1180, - margin: "0 auto", - }, -}); - -type Props = PropsWithChildren<{}>; - -export function AppColumnsLayout({ children }: Props) { - const styles = useStyles(); - - return
{children}
; +export function AppColumnsLayout({ children }: PropsWithChildren<{}>) { + return ( + + {children} + + ); } diff --git a/apps/klaviyo/src/pages/_app.tsx b/apps/klaviyo/src/pages/_app.tsx index 1af174f..3040f90 100644 --- a/apps/klaviyo/src/pages/_app.tsx +++ b/apps/klaviyo/src/pages/_app.tsx @@ -1,92 +1,24 @@ -import "@saleor/apps-shared/src/globals.css"; - -import { StylesProvider, Theme } from "@material-ui/core/styles"; +import "@saleor/macaw-ui/next/style"; import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge"; -import { - dark, - light, - SaleorThemeColors, - ThemeProvider as MacawUIThemeProvider, -} from "@saleor/macaw-ui"; -import React, { PropsWithChildren, useEffect } from "react"; - +import React from "react"; +import { AppProps } from "next/app"; +import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next"; +import { Box, ThemeProvider } from "@saleor/macaw-ui/next"; +import { NoSSRWrapper } from "@saleor/apps-shared"; import { ThemeSynchronizer } from "../hooks/theme-synchronizer"; -import { AppLayoutProps } from "../../types"; -import { createGenerateClassName } from "@material-ui/core"; - -type PalettesOverride = Record<"light" | "dark", SaleorThemeColors>; /** - * Temporary override of colors, to match new dashboard palette. - * Long term this will be replaced with Macaw UI 2.x with up to date design tokens + * Ensure instance is a singleton. */ -const palettes: PalettesOverride = { - light: { - ...light, - background: { - default: "#fff", - paper: "#fff", - }, - }, - dark: { - ...dark, - background: { - default: "hsla(211, 42%, 14%, 1)", - paper: "hsla(211, 42%, 14%, 1)", - }, - }, -}; - -const themeOverrides: Partial = { - overrides: { - MuiTableCell: { - body: { - paddingBottom: 8, - paddingTop: 8, - }, - root: { - height: 56, - paddingBottom: 4, - paddingTop: 4, - }, - }, - }, -}; - -const generateClassName = createGenerateClassName({ - productionPrefix: "c", - disableGlobal: true, -}); - -/** - * Ensure instance is a singleton, so React 18 dev mode doesn't render it twice - */ -const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined; - -// That's a hack required by Macaw-UI incompatibility with React@18 -const ThemeProvider = MacawUIThemeProvider as React.FC< - PropsWithChildren<{ overrides?: Partial; ssr: boolean; palettes: PalettesOverride }> ->; - -function SaleorApp({ Component, pageProps }: AppLayoutProps) { - const getLayout = Component.getLayout ?? ((page) => page); - - useEffect(() => { - const jssStyles = document.querySelector("#jss-server-side"); - - if (jssStyles) { - jssStyles?.parentElement?.removeChild(jssStyles); - } - }, []); +export const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined; +function SaleorApp({ Component, pageProps }: AppProps) { return ( - - - - {getLayout()} - - + + + + ); } diff --git a/apps/klaviyo/src/pages/configuration.tsx b/apps/klaviyo/src/pages/configuration.tsx index 21c5e77..702151d 100644 --- a/apps/klaviyo/src/pages/configuration.tsx +++ b/apps/klaviyo/src/pages/configuration.tsx @@ -1,32 +1,20 @@ -import { Link, List, ListItem, Paper, PaperProps, TextField, Typography } from "@material-ui/core"; -import Skeleton from "@material-ui/lab/Skeleton"; import { useAppBridge, withAuthorization } from "@saleor/app-sdk/app-bridge"; import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const"; -import { ConfirmButton, ConfirmButtonTransitionState, makeStyles } from "@saleor/macaw-ui"; import { ChangeEvent, SyntheticEvent, useEffect, useState } from "react"; -import { AccessWarning } from "../components/AccessWarning/AccessWarning"; import { useAppApi } from "../hooks/useAppApi"; import { AppColumnsLayout } from "../lib/ui/app-columns-layout"; import { useDashboardNotification } from "@saleor/apps-shared"; +import { Box, BoxProps, Text, Input, Button } from "@saleor/macaw-ui/next"; interface ConfigurationField { key: string; value: string; } -const useStyles = makeStyles((theme) => ({ - confirmButton: { - marginLeft: "auto", - }, - fieldContainer: { - marginBottom: theme.spacing(2), - }, -})); - -function Section(props: PaperProps) { - return ; +function Section(props: BoxProps) { + return ; } function Instructions() { @@ -46,22 +34,22 @@ function Instructions() { return (
- + How to set up - - - App will send events as Klaviyo metrics each time Saleor Event occurs. - - + + App will send events as Klaviyo metrics each time Saleor Event occurs. + When first metric is sent, it should be available in Klaviyo to build on top of. - - + + Metric name can be customized, PUBLIC_TOKEN must be provided to enable the app. - - Useful links - - - + + Useful links + + + + How to configure + +
); } function Configuration() { const { appBridgeState } = useAppBridge(); - const classes = useStyles(); const { notifySuccess, notifyError } = useDashboardNotification(); const [configuration, setConfiguration] = useState(); - const [transitionState, setTransitionState] = useState("default"); const { data: configurationData, error } = useAppApi({ url: "/api/configuration", @@ -142,7 +130,6 @@ function Configuration() { */ const handleSubmit = (event: SyntheticEvent) => { event.preventDefault(); - setTransitionState("loading"); fetch("/api/configuration", { method: "POST", @@ -157,12 +144,10 @@ function Configuration() { if (response.status !== 200) { throw new Error("Error saving configuration data"); } - setTransitionState("success"); notifySuccess("Success", "Configuration updated successfully"); }) .catch(async () => { - setTransitionState("error"); await notifyError( "Configuration update failed. Ensure fields are filled correctly and you have MANAGE_APPS permission" ); @@ -207,41 +192,32 @@ function Configuration() { } if (configuration === undefined) { - return ; + return

Loading...

; } return (
-
+ + Klaviyo configuration + + {configuration!.map(({ key, value }) => ( -
- +
+
))}
- +
- +
); } -export default withAuthorization({ - notIframe: , - unmounted: null, - noDashboardToken: , - dashboardTokenInvalid: , -})(Configuration); +export default Configuration; diff --git a/apps/klaviyo/src/pages/index.tsx b/apps/klaviyo/src/pages/index.tsx new file mode 100644 index 0000000..9406c78 --- /dev/null +++ b/apps/klaviyo/src/pages/index.tsx @@ -0,0 +1,32 @@ +import { NextPage } from "next"; +import { useAppBridge } from "@saleor/app-sdk/app-bridge"; +import { useEffect } from "react"; +import { useIsMounted } from "usehooks-ts"; +import { useRouter } from "next/router"; +import { isInIframe } from "@saleor/apps-shared"; + +const IndexPage: NextPage = () => { + const { appBridgeState } = useAppBridge(); + const isMounted = useIsMounted(); + const { replace } = useRouter(); + + useEffect(() => { + if (isMounted() && appBridgeState?.ready) { + replace("/configuration"); + } + }, [isMounted, appBridgeState?.ready, replace]); + + if (isInIframe()) { + return Loading...; + } + + return ( +
+

Saleor Klaviyo

+

This is Saleor App that allows to use external service to handle taxes.

+

Install the app in your Saleor instance and open it in Dashboard.

+
+ ); +}; + +export default IndexPage; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55db11d..059ae09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -769,8 +769,8 @@ importers: specifier: workspace:* version: link:../../packages/shared '@saleor/macaw-ui': - specifier: ^0.7.2 - version: 0.7.3(@material-ui/core@4.12.4)(@material-ui/icons@4.11.3)(@material-ui/lab@4.0.0-alpha.61)(@types/react@18.2.5)(react-dom@18.2.0)(react-helmet@6.1.0)(react@18.2.0) + specifier: 0.8.0-pre.95 + version: 0.8.0-pre.95(@types/react-dom@18.2.5)(@types/react@18.2.5)(react-dom@18.2.0)(react@18.2.0) '@sentry/nextjs': specifier: 7.55.2 version: 7.55.2(next@13.3.0)(react@18.2.0) @@ -810,6 +810,9 @@ importers: urql: specifier: ^4.0.4 version: 4.0.4(graphql@16.6.0)(react@18.2.0) + usehooks-ts: + specifier: ^2.9.1 + version: 2.9.1(react-dom@18.2.0)(react@18.2.0) vite: specifier: 4.3.9 version: 4.3.9(@types/node@18.15.3)