Compare commits
3 commits
main
...
slack-maca
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7946576714 | ||
![]() |
2b27c0c463 | ||
![]() |
0fb3108321 |
12 changed files with 128 additions and 420 deletions
5
.changeset/plenty-mayflies-enjoy.md
Normal file
5
.changeset/plenty-mayflies-enjoy.md
Normal file
|
@ -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.
|
|
@ -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/macaw-ui": "0.8.0-pre.95",
|
||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||
"@sentry/nextjs": "7.55.2",
|
||||
"@urql/exchange-auth": "^2.1.4",
|
||||
"clsx": "^1.2.1",
|
||||
|
|
|
@ -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<WarningCause, string> = {
|
|||
|
||||
export function AccessWarning({ cause = "unknown_cause" }: AccessWarningProps) {
|
||||
return (
|
||||
<div suppressHydrationWarning>
|
||||
<Typography variant="subtitle1">
|
||||
<div>
|
||||
<Text as={"h2"} variant="heading">
|
||||
App can't be accessed outside of the Saleor Dashboard
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" style={{ marginTop: "2rem" }}>
|
||||
</Text>
|
||||
<Text variant="body" style={{ marginTop: "2rem" }}>
|
||||
❌ {warnings[cause]}
|
||||
</Typography>
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 <div className={styles.root}>{children}</div>;
|
||||
return (
|
||||
<Box
|
||||
paddingX={8}
|
||||
display={"grid"}
|
||||
marginTop={8}
|
||||
__maxWidth={"1180px"}
|
||||
__gridTemplateColumns={"380px auto"}
|
||||
gap={8}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 (
|
||||
<div className={classes.loaderContainer}>
|
||||
<CircularProgress size={100} />
|
||||
|
||||
<Typography variant="subtitle1" className={classes.message}>
|
||||
Attempting connection to Saleor Dashboard
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -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 };
|
|
@ -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);
|
||||
|
|
|
@ -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<Theme> = {
|
||||
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<Theme>; 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 (
|
||||
<NoSSRWrapper>
|
||||
<AppBridgeProvider appBridgeInstance={appBridgeInstance}>
|
||||
<ThemeProvider palettes={palettes} overrides={themeOverrides} ssr>
|
||||
<ThemeProvider>
|
||||
<ThemeSynchronizer />
|
||||
<RoutePropagator />
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ThemeProvider>
|
||||
</AppBridgeProvider>
|
||||
|
|
|
@ -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 <NextErrorComponent statusCode={props.statusCode} />;
|
||||
};
|
||||
|
||||
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;
|
|
@ -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<ConfigurationField[]>();
|
||||
const [transitionState, setTransitionState] = useState<ConfirmButtonTransitionState>("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 <Skeleton />;
|
||||
return <Text>Loading</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{configuration!.map(({ key, value }) => (
|
||||
<div key={key} className={classes.fieldContainer}>
|
||||
<TextField label={key} name={key} fullWidth onChange={onChange} value={value} />
|
||||
</div>
|
||||
))}
|
||||
<p className={classes.additionalInfo}>
|
||||
This webhook will be called when new order is created and `order_created` event is
|
||||
triggered.
|
||||
</p>
|
||||
<div>
|
||||
<ConfirmButton
|
||||
type="submit"
|
||||
variant="primary"
|
||||
fullWidth
|
||||
transitionState={transitionState}
|
||||
labels={{
|
||||
confirm: "Save",
|
||||
error: "Error",
|
||||
}}
|
||||
className={classes.confirmButton}
|
||||
<div key={key}>
|
||||
<Input
|
||||
label={key}
|
||||
name={key}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
helperText={
|
||||
"This webhook will be called when new order is created and `order_created` event is triggered."
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Box marginTop={4}>
|
||||
<Button type="submit" variant="primary">
|
||||
Save
|
||||
</Button>
|
||||
</Box>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
@ -147,50 +118,29 @@ function Instructions() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Typography>How to configure</Typography>
|
||||
<List>
|
||||
<ListItem>
|
||||
<Link
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
openExternalUrl(slackUrl.href);
|
||||
}}
|
||||
href={slackUrl.href}
|
||||
>
|
||||
Install Slack application
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
Copy incoming Webhook URL from Slack app configuration and paste it below into
|
||||
`WEBHOOK_URL` field
|
||||
</ListItem>
|
||||
<ListItem>Save configuration</ListItem>
|
||||
</List>
|
||||
<Typography>Useful links</Typography>
|
||||
<List>
|
||||
<ListItem>
|
||||
<Link
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
openExternalUrl("https://github.com/saleor/saleor-app-slack");
|
||||
}}
|
||||
href="https://github.com/saleor/saleor-app-slack"
|
||||
>
|
||||
Visit repository & readme
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Link
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
openExternalUrl("https://api.slack.com/messaging/webhooks");
|
||||
}}
|
||||
href="https://api.slack.com/messaging/webhooks"
|
||||
>
|
||||
<Text variant={"heading"}>How to configure</Text>
|
||||
<Box display={"flex"} gap={2} as={"ul"} flexDirection={"column"}>
|
||||
<li>
|
||||
<TextLink href={slackUrl.href}>1. Install Slack application</TextLink>
|
||||
</li>
|
||||
<li>
|
||||
<Text>
|
||||
2. Copy incoming Webhook URL from Slack app configuration and paste it below into{" "}
|
||||
<Text variant={"bodyStrong"}>WEBHOOK_URL</Text> field
|
||||
</Text>
|
||||
</li>
|
||||
<li>
|
||||
<Text>3. Save configuration</Text>
|
||||
</li>
|
||||
</Box>
|
||||
<Text variant={"heading"}>Useful links</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<TextLink newTab href={"https://api.slack.com/messaging/webhooks"}>
|
||||
Read about Slack apps that use incoming webhooks
|
||||
</Link>
|
||||
</ListItem>
|
||||
</List>
|
||||
</TextLink>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -204,17 +154,21 @@ const ConfigurationWithAuth = withAuthorization({
|
|||
|
||||
ConfigurationWithAuth.getLayout = (page: ReactElement) => (
|
||||
<AppColumnsLayout>
|
||||
<div />
|
||||
<Card>
|
||||
<CardHeader title="Configuration" />
|
||||
<CardContent>{page}</CardContent>
|
||||
</Card>
|
||||
<Card style={{ marginBottom: 40 }}>
|
||||
<CardHeader title="Instructions" />
|
||||
<CardContent>
|
||||
<Box marginBottom={4}>
|
||||
<Instructions />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box
|
||||
borderColor={"neutralHighlight"}
|
||||
borderStyle={"solid"}
|
||||
borderWidth={1}
|
||||
padding={4}
|
||||
borderRadius={4}
|
||||
>
|
||||
<Text as={"h2"} marginBottom={4} variant={"heading"}>
|
||||
Configuration
|
||||
</Text>
|
||||
<Box>{page}</Box>
|
||||
</Box>
|
||||
</AppColumnsLayout>
|
||||
);
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<TextField
|
||||
label="Your Saleor URL"
|
||||
fullWidth
|
||||
name="saleor-url"
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end">.saleor.cloud</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<HTMLFormElement> = (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 <LinearProgress />;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Image
|
||||
alt="Saleor logo"
|
||||
width={200}
|
||||
src={themeType === "light" ? SaleorLogoImage : SaleorLogoImageDark}
|
||||
/>
|
||||
<Typography className={styles.headline} variant="h1">
|
||||
The Slack App has to be <br />
|
||||
launched in the Saleor Dashboard
|
||||
</Typography>
|
||||
<div className={styles.grid}>
|
||||
<div>
|
||||
<Typography variant="h3">
|
||||
Provide you Saleor URL
|
||||
<br /> to quickly install the app
|
||||
</Typography>
|
||||
<form onSubmit={onFormSubmit} className={styles.form}>
|
||||
<Input />
|
||||
<Button type="submit" className={styles.submitButton} fullWidth variant="primary">
|
||||
{" "}
|
||||
Submit and start installation
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="h3">
|
||||
Or check the instructions
|
||||
<br /> and see how to install the app
|
||||
</Typography>
|
||||
<div className={styles.buttons}>
|
||||
<Button variant="secondary" fullWidth>
|
||||
Open repository
|
||||
</Button>
|
||||
<Button variant="secondary" fullWidth>
|
||||
See Saleor Docs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Box>
|
||||
<Text as={"h1"} variant={"hero"}>
|
||||
Saleor Slack App
|
||||
</Text>
|
||||
<Text as={"p"}>This is Saleor App that allows invoices generation</Text>
|
||||
<Text as={"p"}>
|
||||
Install app in your Saleor instance and open in with Dashboard{" "}
|
||||
<a href={"https://github.com/saleor/apps"}>or check it on GitHub</a>
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue