From 57fea9166e1ee3cd23a99a07483ac09165583341 Mon Sep 17 00:00:00 2001 From: Lukasz Ostrowski Date: Tue, 27 Jun 2023 12:07:24 +0200 Subject: [PATCH] Redesign Slack app to use new Macaw / next (#671) * Replace material with new Macaw ui * Rewrtirren Slack to the new macaw * CR fixes and imprve layour --- .changeset/plenty-mayflies-enjoy.md | 5 + apps/slack/package.json | 4 +- .../AccessWarning/AccessWarning.tsx | 12 +- .../AppColumnsLayout/AppColumnsLayout.tsx | 28 ++- .../components/LoadingPage/LoadingPage.tsx | 18 -- .../src/components/LoadingPage/styles.ts | 14 -- apps/slack/src/hooks/theme-synchronizer.tsx | 39 ++--- apps/slack/src/pages/_app.tsx | 82 +-------- apps/slack/src/pages/_error.js | 43 ----- apps/slack/src/pages/configuration.tsx | 162 +++++++----------- apps/slack/src/pages/index.tsx | 131 ++------------ pnpm-lock.yaml | 10 +- 12 files changed, 128 insertions(+), 420 deletions(-) create mode 100644 .changeset/plenty-mayflies-enjoy.md delete mode 100644 apps/slack/src/components/LoadingPage/LoadingPage.tsx delete mode 100644 apps/slack/src/components/LoadingPage/styles.ts delete mode 100644 apps/slack/src/pages/_error.js diff --git a/.changeset/plenty-mayflies-enjoy.md b/.changeset/plenty-mayflies-enjoy.md new file mode 100644 index 0000000..b40c7f5 --- /dev/null +++ b/.changeset/plenty-mayflies-enjoy.md @@ -0,0 +1,5 @@ +--- +"saleor-app-slack": minor +--- + +Rewritten UI to use modern macaw UI, instead legacy one with Material UI. There are slight changes in typography and spacings, but no new changes were introduced. diff --git a/apps/slack/package.json b/apps/slack/package.json index 0daa0fb..fc8b5cc 100644 --- a/apps/slack/package.json +++ b/apps/slack/package.json @@ -16,7 +16,9 @@ "@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/apps-ui": "workspace:*", + "@saleor/react-hook-form-macaw": "workspace:*", + "@saleor/macaw-ui": "0.8.0-pre.95", "@sentry/nextjs": "7.55.2", "@urql/exchange-auth": "^2.1.4", "clsx": "^1.2.1", diff --git a/apps/slack/src/components/AccessWarning/AccessWarning.tsx b/apps/slack/src/components/AccessWarning/AccessWarning.tsx index 1091756..ae3dc24 100644 --- a/apps/slack/src/components/AccessWarning/AccessWarning.tsx +++ b/apps/slack/src/components/AccessWarning/AccessWarning.tsx @@ -1,5 +1,5 @@ -import { Typography } from "@material-ui/core"; import React from "react"; +import { Text } from "@saleor/macaw-ui/next"; type WarningCause = | "not_in_iframe" @@ -20,13 +20,13 @@ const warnings: Record = { export function AccessWarning({ cause = "unknown_cause" }: AccessWarningProps) { return ( -
- +
+ App can't be accessed outside of the Saleor Dashboard - - + + ❌ {warnings[cause]} - +
); } diff --git a/apps/slack/src/components/AppColumnsLayout/AppColumnsLayout.tsx b/apps/slack/src/components/AppColumnsLayout/AppColumnsLayout.tsx index 94ee503..63db4fb 100644 --- a/apps/slack/src/components/AppColumnsLayout/AppColumnsLayout.tsx +++ b/apps/slack/src/components/AppColumnsLayout/AppColumnsLayout.tsx @@ -1,21 +1,19 @@ -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 const AppColumnsLayout = ({ children }: Props) => { - const styles = useStyles(); - - return
{children}
; + return ( + + {children} + + ); }; diff --git a/apps/slack/src/components/LoadingPage/LoadingPage.tsx b/apps/slack/src/components/LoadingPage/LoadingPage.tsx deleted file mode 100644 index a87ee61..0000000 --- a/apps/slack/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"; - -function LoadingPage() { - const classes = useStyles(); - - return ( -
- - - - Attempting connection to Saleor Dashboard - -
- ); -} diff --git a/apps/slack/src/components/LoadingPage/styles.ts b/apps/slack/src/components/LoadingPage/styles.ts deleted file mode 100644 index b392343..0000000 --- a/apps/slack/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/slack/src/hooks/theme-synchronizer.tsx b/apps/slack/src/hooks/theme-synchronizer.tsx index 03a4dff..2ae0fe6 100644 --- a/apps/slack/src/hooks/theme-synchronizer.tsx +++ b/apps/slack/src/hooks/theme-synchronizer.tsx @@ -1,40 +1,25 @@ import { useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { useTheme } from "@saleor/macaw-ui"; +import { useTheme } from "@saleor/macaw-ui/next"; import { memo, 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() { - const { appBridgeState, appBridge } = useAppBridge(); - const { setTheme, themeType } = useTheme(); - - /* - * todo - replace this hook to appBridge.subscribe and react only only on initial theme event - * useEffect(() =>{ - * appBridge?.subscribe('theme',console.log) - * },[appBridge]) - */ +// todo move to shared +export function ThemeSynchronizer() { + const { appBridgeState } = useAppBridge(); + 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/slack/src/pages/_app.tsx b/apps/slack/src/pages/_app.tsx index 905be19..e6b4da8 100644 --- a/apps/slack/src/pages/_app.tsx +++ b/apps/slack/src/pages/_app.tsx @@ -1,90 +1,26 @@ -import "../styles/globals.css"; - -import { Theme } from "@material-ui/core/styles"; +import "@saleor/macaw-ui/next/style"; import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge"; +import React from "react"; +import { AppProps } from "next/app"; import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next"; -import { - dark, - light, - SaleorThemeColors, - ThemeProvider as MacawUIThemeProvider, -} from "@saleor/macaw-ui"; -import React, { PropsWithChildren, useEffect } from "react"; - -import { AppLayoutProps } from "../../types"; +import { Box, ThemeProvider } from "@saleor/macaw-ui/next"; +import { NoSSRWrapper } from "@saleor/apps-shared"; import { ThemeSynchronizer } from "../hooks/theme-synchronizer"; -import { NoSSRWrapper } from "../lib/no-ssr-wrapper"; - -const themeOverrides: Partial = { - overrides: { - MuiTableCell: { - body: { - paddingBottom: 8, - paddingTop: 8, - }, - root: { - height: 56, - paddingBottom: 4, - paddingTop: 4, - }, - }, - }, -}; /** * Ensure instance is a singleton. - * TODO: This is React 18 issue, consider hiding this workaround inside app-sdk */ -const appBridgeInstance = - typeof window !== "undefined" ? new AppBridge({ autoNotifyReady: false }) : undefined; +export const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined; -type PalettesOverride = Record<"light" | "dark", SaleorThemeColors>; - -/** - * Temporary override of colors, to match new dashboard palette. - * Long term this will be replaced with Macaw UI 2.x with up to date design tokens - */ -const palettes: PalettesOverride = { - light: { - ...light, - background: { - default: "#fff", - paper: "#fff", - }, - }, - dark: { - ...dark, - background: { - default: "hsla(211, 42%, 14%, 1)", - paper: "hsla(211, 42%, 14%, 1)", - }, - }, -}; - -/** - * 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) { +function SaleorApp({ Component, pageProps }: AppProps) { + // @ts-ignore todo refactor const getLayout = Component.getLayout ?? ((page) => page); - useEffect(() => { - const jssStyles = document.querySelector("#jss-server-side"); - - if (jssStyles) { - jssStyles?.parentElement?.removeChild(jssStyles); - } - }, []); - return ( - + - {getLayout()} diff --git a/apps/slack/src/pages/_error.js b/apps/slack/src/pages/_error.js deleted file mode 100644 index 316beec..0000000 --- a/apps/slack/src/pages/_error.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * NOTE: This requires `@sentry/nextjs` version 7.3.0 or higher. - * - * NOTE: If using this with `next` version 12.2.0 or lower, uncomment the - * penultimate line in `CustomErrorComponent`. - * - * This page is loaded by Nextjs: - * - on the server, when data-fetching methods throw or reject - * - on the client, when `getInitialProps` throws or rejects - * - on the client, when a React lifecycle method throws or rejects, and it's - * caught by the built-in Nextjs error boundary - * - * See: - * - https://nextjs.org/docs/basic-features/data-fetching/overview - * - https://nextjs.org/docs/api-reference/data-fetching/get-initial-props - * - https://reactjs.org/docs/error-boundaries.html - */ - -import * as Sentry from "@sentry/nextjs"; -import NextErrorComponent from "next/error"; - -const CustomErrorComponent = (props) => { - /* - * If you're using a Nextjs version prior to 12.2.1, uncomment this to - * compensate for https://github.com/vercel/next.js/issues/8592 - * Sentry.captureUnderscoreErrorException(props); - */ - - return ; -}; - -CustomErrorComponent.getInitialProps = async (contextData) => { - /* - * In case this is running in a serverless function, await this in order to give Sentry - * time to send the error before the lambda exits - */ - await Sentry.captureUnderscoreErrorException(contextData); - - // This will contain the status code of the response - return NextErrorComponent.getInitialProps(contextData); -}; - -export default CustomErrorComponent; diff --git a/apps/slack/src/pages/configuration.tsx b/apps/slack/src/pages/configuration.tsx index 7d81ad0..36e0bf3 100644 --- a/apps/slack/src/pages/configuration.tsx +++ b/apps/slack/src/pages/configuration.tsx @@ -1,48 +1,26 @@ -import { - Card, - CardContent, - CardHeader, - Link, - List, - ListItem, - 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, ReactElement, SyntheticEvent, useEffect, useState } from "react"; -import { AccessWarning } from "../components/AccessWarning/AccessWarning"; import { ConfigurationError } from "../components/ConfigurationError/ConfigurationError"; import { useAppApi } from "../hooks/useAppApi"; import { AppColumnsLayout } from "../components/AppColumnsLayout/AppColumnsLayout"; import { useDashboardNotification } from "@saleor/apps-shared"; +import { Input, Text, Box, Button } from "@saleor/macaw-ui/next"; + +import { TextLink } from "@saleor/apps-ui"; +import { AccessWarning } from "../components/AccessWarning/AccessWarning"; + interface ConfigurationField { key: string; value: string; } -const useStyles = makeStyles((theme) => ({ - confirmButton: { - marginLeft: "auto", - }, - fieldContainer: { - marginBottom: theme.spacing(2), - }, - additionalInfo: { - marginBottom: theme.spacing(3), - }, -})); - function Configuration() { - const classes = useStyles(); const { appBridgeState } = useAppBridge(); const { notifyError, notifySuccess } = useDashboardNotification(); const [configuration, setConfiguration] = useState(); - const [transitionState, setTransitionState] = useState("default"); const { data: configurationData, error } = useAppApi<{ data: ConfigurationField[] }>({ url: "/api/configuration", @@ -56,7 +34,6 @@ function Configuration() { const handleSubmit = (event: SyntheticEvent) => { event.preventDefault(); - setTransitionState("loading"); fetch("/api/configuration", { method: "POST", @@ -68,11 +45,9 @@ function Configuration() { body: JSON.stringify({ data: configuration }), }) .then(async (response) => { - setTransitionState(response.status === 200 ? "success" : "error"); notifySuccess("Success", "Configuration updated successfully"); }) .catch(async () => { - setTransitionState("error"); await notifyError("Configuration update failed"); }); }; @@ -91,33 +66,29 @@ function Configuration() { } if (configuration === undefined) { - return ; + return Loading; } return (
{configuration!.map(({ key, value }) => ( -
- +
+
))} -

- This webhook will be called when new order is created and `order_created` event is - triggered. -

-
- -
+ + + ); } @@ -147,50 +118,29 @@ function Instructions() { return ( <> - How to configure - - - { - e.preventDefault(); - openExternalUrl(slackUrl.href); - }} - href={slackUrl.href} - > - Install Slack application - - - - Copy incoming Webhook URL from Slack app configuration and paste it below into - `WEBHOOK_URL` field - - Save configuration - - Useful links - - - { - e.preventDefault(); - openExternalUrl("https://github.com/saleor/saleor-app-slack"); - }} - href="https://github.com/saleor/saleor-app-slack" - > - Visit repository & readme - - - - { - e.preventDefault(); - openExternalUrl("https://api.slack.com/messaging/webhooks"); - }} - href="https://api.slack.com/messaging/webhooks" - > + How to configure + +
  • + 1. Install Slack application +
  • +
  • + + 2. Copy incoming Webhook URL from Slack app configuration and paste it below into{" "} + WEBHOOK_URL field + +
  • +
  • + 3. Save configuration +
  • +
    + Useful links +
      +
    • + Read about Slack apps that use incoming webhooks - - - + +
    • +
    ); } @@ -204,17 +154,21 @@ const ConfigurationWithAuth = withAuthorization({ ConfigurationWithAuth.getLayout = (page: ReactElement) => ( -
    - - - {page} - - - - - - - + + + + + + Configuration + + {page} + ); diff --git a/apps/slack/src/pages/index.tsx b/apps/slack/src/pages/index.tsx index 7f7d488..8b93427 100644 --- a/apps/slack/src/pages/index.tsx +++ b/apps/slack/src/pages/index.tsx @@ -1,85 +1,15 @@ import { NextPage } from "next"; import { useAppBridge } from "@saleor/app-sdk/app-bridge"; -import { useRouter } from "next/router"; -import React, { FormEventHandler, useEffect } from "react"; +import { useEffect } from "react"; import { useIsMounted } from "usehooks-ts"; -import Image from "next/image"; -import SaleorLogoImage from "../assets/saleor-logo.svg"; -import SaleorLogoImageDark from "../assets/saleor-logo-dark.svg"; -import { InputAdornment, LinearProgress, TextField, Typography } from "@material-ui/core"; -import { Button, makeStyles, useTheme } from "@saleor/macaw-ui"; +import { useRouter } from "next/router"; import { isInIframe } from "@saleor/apps-shared"; +import { Box, Text } from "@saleor/macaw-ui/next"; -const useStyles = makeStyles({ - root: { - maxWidth: 960, - margin: "50px auto", - }, - headline: { - marginTop: 70, - lineHeight: 1.5, - }, - grid: { - display: "grid", - gridTemplateColumns: "50% 50%", - gap: 116, - marginTop: 48, - }, - form: { - marginTop: 24, - }, - submitButton: { - marginTop: 12, - }, - buttons: { - marginTop: 24, - "& > *": { - marginBottom: 12, - }, - }, -}); - -const Input = () => { - return ( - .saleor.cloud, - }} - /> - ); -}; - -/** - * Common landing page in case of app being open outside the Dashboard. - * Allows quick installation with Saleor env input - * - * Can be safely removed - */ const IndexPage: NextPage = () => { - const styles = useStyles(); const { appBridgeState } = useAppBridge(); const isMounted = useIsMounted(); const { replace } = useRouter(); - const { themeType } = useTheme(); - - const onFormSubmit: FormEventHandler = (e) => { - e.preventDefault(); - - const appOrigin = window.location.origin; - const appManifestUrl = new URL("api/manifest", appOrigin); - const saleorUrlSlug = new FormData(e.currentTarget).get("saleor-url") as string; - const saleorUrl = new URL("https://" + saleorUrlSlug.replace("https://", "") + ".saleor.cloud"); - - const installationUrl = new URL( - `dashboard/apps/install?manifestUrl=${appManifestUrl}`, - saleorUrl - ).href; - - window.location.href = installationUrl; - }; useEffect(() => { if (isMounted() && appBridgeState?.ready) { @@ -87,54 +17,21 @@ const IndexPage: NextPage = () => { } }, [isMounted, appBridgeState?.ready]); - /** - * TODO Check also some timeout and show error if appBridge never handshakes - */ if (isInIframe()) { - return ; + return null; } return ( -
    - Saleor logo - - The Slack App has to be
    - launched in the Saleor Dashboard -
    -
    -
    - - Provide you Saleor URL -
    to quickly install the app -
    -
    - - -
    -
    -
    - - Or check the instructions -
    and see how to install the app -
    -
    - - -
    -
    -
    -
    + + + Saleor Slack App + + This is Saleor App that allows invoices generation + + Install app in your Saleor instance and open in with Dashboard{" "} + or check it on GitHub + + ); }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be7aecd..6ecb40b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1273,9 +1273,15 @@ importers: '@saleor/apps-shared': specifier: workspace:* version: link:../../packages/shared + '@saleor/apps-ui': + specifier: workspace:* + version: link:../../packages/ui '@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) + '@saleor/react-hook-form-macaw': + specifier: workspace:* + version: link:../../packages/react-hook-form-macaw '@sentry/nextjs': specifier: 7.55.2 version: 7.55.2(next@13.3.0)(react@18.2.0)