2023-02-09 10:58:20 +00:00
|
|
|
import { useAppBridge, withAuthorization } from "@saleor/app-sdk/app-bridge";
|
|
|
|
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
|
|
|
import { ChangeEvent, ReactElement, SyntheticEvent, useEffect, useState } from "react";
|
|
|
|
|
|
|
|
import { ConfigurationError } from "../components/ConfigurationError/ConfigurationError";
|
2023-04-05 18:27:23 +00:00
|
|
|
import { useAppApi } from "../hooks/useAppApi";
|
2023-02-09 10:58:20 +00:00
|
|
|
import { AppColumnsLayout } from "../components/AppColumnsLayout/AppColumnsLayout";
|
2023-04-14 15:40:49 +00:00
|
|
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
2023-02-09 10:58:20 +00:00
|
|
|
|
2023-06-22 08:32:25 +00:00
|
|
|
import { Input, Text, Box, Button } from "@saleor/macaw-ui/next";
|
|
|
|
import { AccessWarning } from "../components/AccessWarning/AccessWarning";
|
|
|
|
|
2023-02-09 10:58:20 +00:00
|
|
|
interface ConfigurationField {
|
|
|
|
key: string;
|
|
|
|
value: string;
|
|
|
|
}
|
|
|
|
|
2023-02-09 14:56:52 +00:00
|
|
|
function Configuration() {
|
2023-02-09 10:58:20 +00:00
|
|
|
const { appBridgeState } = useAppBridge();
|
2023-04-14 15:40:49 +00:00
|
|
|
const { notifyError, notifySuccess } = useDashboardNotification();
|
2023-02-09 10:58:20 +00:00
|
|
|
const [configuration, setConfiguration] = useState<ConfigurationField[]>();
|
|
|
|
|
|
|
|
const { data: configurationData, error } = useAppApi<{ data: ConfigurationField[] }>({
|
|
|
|
url: "/api/configuration",
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (configurationData && !configuration) {
|
|
|
|
setConfiguration(configurationData.data);
|
|
|
|
}
|
|
|
|
}, [configurationData, configuration]);
|
|
|
|
|
|
|
|
const handleSubmit = (event: SyntheticEvent) => {
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
fetch("/api/configuration", {
|
|
|
|
method: "POST",
|
|
|
|
headers: [
|
|
|
|
["content-type", "application/json"],
|
|
|
|
[SALEOR_API_URL_HEADER, appBridgeState?.saleorApiUrl!],
|
|
|
|
[SALEOR_AUTHORIZATION_BEARER_HEADER, appBridgeState?.token!],
|
|
|
|
],
|
|
|
|
body: JSON.stringify({ data: configuration }),
|
|
|
|
})
|
|
|
|
.then(async (response) => {
|
2023-04-14 15:40:49 +00:00
|
|
|
notifySuccess("Success", "Configuration updated successfully");
|
2023-02-09 10:58:20 +00:00
|
|
|
})
|
|
|
|
.catch(async () => {
|
2023-04-14 15:40:49 +00:00
|
|
|
await notifyError("Configuration update failed");
|
2023-02-09 10:58:20 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const onChange = (event: ChangeEvent) => {
|
|
|
|
const { name, value } = event.target as HTMLInputElement;
|
2023-04-18 13:10:00 +00:00
|
|
|
|
2023-02-09 10:58:20 +00:00
|
|
|
setConfiguration((prev) =>
|
|
|
|
prev!.map((prevField) => (prevField.key === name ? { ...prevField, value } : prevField))
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
console.error("Can't establish connection with the App API: ", error);
|
2023-02-09 14:56:52 +00:00
|
|
|
return <ConfigurationError />;
|
2023-02-09 10:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (configuration === undefined) {
|
2023-06-22 08:32:25 +00:00
|
|
|
return <Text>Loading</Text>;
|
2023-02-09 10:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
{configuration!.map(({ key, value }) => (
|
2023-06-22 08:32:25 +00:00
|
|
|
<div key={key}>
|
2023-06-22 08:47:39 +00:00
|
|
|
<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."
|
|
|
|
}
|
|
|
|
/>
|
2023-02-09 10:58:20 +00:00
|
|
|
</div>
|
|
|
|
))}
|
2023-06-22 08:47:39 +00:00
|
|
|
<Box marginTop={4}>
|
2023-06-22 08:32:25 +00:00
|
|
|
<Button type="submit" variant="primary">
|
|
|
|
Save
|
|
|
|
</Button>
|
2023-06-22 08:47:39 +00:00
|
|
|
</Box>
|
2023-02-09 10:58:20 +00:00
|
|
|
</form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function Instructions() {
|
|
|
|
const { appBridge } = useAppBridge();
|
|
|
|
|
|
|
|
const { data } = useAppApi({
|
|
|
|
url: "/api/slack-app-manifest",
|
|
|
|
});
|
|
|
|
|
|
|
|
const slackUrl = new URL("https://api.slack.com/apps");
|
2023-04-18 13:10:00 +00:00
|
|
|
|
2023-02-09 10:58:20 +00:00
|
|
|
slackUrl.searchParams.append("new_app", "1");
|
|
|
|
slackUrl.searchParams.append("manifest_json", JSON.stringify(data));
|
|
|
|
|
|
|
|
const openExternalUrl = (to: string) => {
|
|
|
|
appBridge?.dispatch({
|
|
|
|
type: "redirect",
|
|
|
|
payload: {
|
|
|
|
newContext: true,
|
|
|
|
actionId: "redirect_from_slack_app",
|
|
|
|
to,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text variant={"bodyStrong"}>How to configure</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a
|
2023-02-09 10:58:20 +00:00
|
|
|
onClick={(e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
openExternalUrl(slackUrl.href);
|
|
|
|
}}
|
|
|
|
href={slackUrl.href}
|
|
|
|
>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text>Install Slack application</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text>
|
|
|
|
Copy incoming Webhook URL from Slack app configuration and paste it below into
|
|
|
|
`WEBHOOK_URL` field
|
|
|
|
</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
</li>
|
|
|
|
<li>Save configuration</li>
|
|
|
|
</ul>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text variant={"bodyStrong"}>Useful links</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a
|
2023-02-09 10:58:20 +00:00
|
|
|
onClick={(e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
openExternalUrl("https://api.slack.com/messaging/webhooks");
|
|
|
|
}}
|
|
|
|
href="https://api.slack.com/messaging/webhooks"
|
|
|
|
>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text>Read about Slack apps that use incoming webhooks</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
2023-02-09 10:58:20 +00:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConfigurationWithAuth = withAuthorization({
|
|
|
|
notIframe: <AccessWarning cause="not_in_iframe" />,
|
|
|
|
unmounted: null,
|
|
|
|
noDashboardToken: <AccessWarning cause="missing_access_token" />,
|
|
|
|
dashboardTokenInvalid: <AccessWarning cause="invalid_access_token" />,
|
|
|
|
})(Configuration);
|
|
|
|
|
|
|
|
ConfigurationWithAuth.getLayout = (page: ReactElement) => (
|
2023-02-28 19:18:34 +00:00
|
|
|
<AppColumnsLayout>
|
|
|
|
<div />
|
2023-06-22 08:32:25 +00:00
|
|
|
<Box>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text as={"h2"} marginBottom={4} variant={"heading"}>
|
|
|
|
Configuration
|
|
|
|
</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
<Box>{page}</Box>
|
|
|
|
</Box>
|
|
|
|
<Box marginBottom={4}>
|
2023-06-22 08:47:39 +00:00
|
|
|
<Text as={"h2"} marginBottom={4} variant={"heading"}>
|
|
|
|
Instructions
|
|
|
|
</Text>
|
2023-06-22 08:32:25 +00:00
|
|
|
<Box>
|
2023-02-28 19:18:34 +00:00
|
|
|
<Instructions />
|
2023-06-22 08:32:25 +00:00
|
|
|
</Box>
|
|
|
|
</Box>
|
2023-02-28 19:18:34 +00:00
|
|
|
</AppColumnsLayout>
|
2023-02-09 10:58:20 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
export default ConfigurationWithAuth;
|