saleor-apps-redis_apl/apps/klaviyo/pages/configuration.tsx
Lukasz Ostrowski 9f843b2d31
Extract TitleBar and AppIcon to shared package and implement it in apps (#134)
* Update imports from apps-shared package

* Extract main bar and app icon

* Remove graphql generated

* Implement AppIcon and MainBar in data importer and invoices

* Change name to TitleBar

* Use TitleBar and AppIcon from shared package

* Use title bar from shared in search

* Refactor slack to use TitleBar from shared package

* Make TitleBar sticky

* Run codegen before cicd tests

* Add generate script
2023-02-09 18:41:23 +01:00

261 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { AppIcon, TitleBar } from "@saleor/apps-shared";
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 useDashboardNotifier from "../utils/useDashboardNotifier";
interface ConfigurationField {
key: string;
value: string;
}
const useStyles = makeStyles((theme) => ({
confirmButton: {
marginLeft: "auto",
},
fieldContainer: {
marginBottom: theme.spacing(2),
},
}));
function Section(props: PaperProps) {
return <Paper style={{ padding: 24 }} elevation={0} {...props} />;
}
function Instructions() {
const { appBridge } = useAppBridge();
const openExternalUrl = (url: string) => {
// eslint-disable-next-line
appBridge?.dispatch({
type: "redirect",
payload: {
newContext: true,
actionId: "redirect_from_klaviyo_app",
to: url,
},
});
};
return (
<Section>
<Typography paragraph variant="h3">
How to set up
</Typography>
<Typography paragraph>
App will send events as Klaviyo metrics each time Saleor Event occurs.
</Typography>
<Typography paragraph>
When first metric is sent, it should be available in Klaviyo to build on top of.
</Typography>
<Typography paragraph>
Metric name can be customized, PUBLIC_TOKEN must be provided to enable the app.
</Typography>
<Typography variant="h3">Useful links</Typography>
<List>
<ListItem>
<Link
onClick={(e) => {
e.preventDefault();
openExternalUrl("https://github.com/saleor/saleor-app-klaviyo");
}}
href="https://github.com/saleor/saleor-app-klaviyo"
>
Visit repository & readme
</Link>
</ListItem>
</List>
<Typography variant="h3">How to configure</Typography>
<List>
<ListItem>
<Link
onClick={(e) => {
e.preventDefault();
openExternalUrl(
"https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys"
);
}}
href="https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys"
>
Read about public tokens
</Link>
</ListItem>
<ListItem>
<Link
onClick={(e) => {
e.preventDefault();
openExternalUrl("https://www.klaviyo.com/account#api-keys-tab");
}}
href="https://www.klaviyo.com/account#api-keys-tab"
>
Get public token here
</Link>
</ListItem>
<ListItem>
<Link
onClick={(e) => {
e.preventDefault();
openExternalUrl(
"https://help.klaviyo.com/hc/en-us/articles/115005076787-Guide-to-Managing-Your-Metrics"
);
}}
href="https://help.klaviyo.com/hc/en-us/articles/115005076787-Guide-to-Managing-Your-Metrics"
>
Read about metrics
</Link>
</ListItem>
</List>
</Section>
);
}
function Configuration() {
const { appBridgeState } = useAppBridge();
const classes = useStyles();
const [notify] = useDashboardNotifier();
const [configuration, setConfiguration] = useState<ConfigurationField[]>();
const [transitionState, setTransitionState] = useState<ConfirmButtonTransitionState>("default");
const { data: configurationData, error } = useAppApi({
url: "/api/configuration",
});
useEffect(() => {
if (configurationData && !configuration) {
setConfiguration(configurationData.data);
}
}, [configurationData, configuration]);
/**
* TODO Rewrite to tRPC
*/
const handleSubmit = (event: SyntheticEvent) => {
event.preventDefault();
setTransitionState("loading");
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) => {
if (response.status !== 200) {
throw new Error("Error saving configuration data");
}
setTransitionState("success");
await notify({
status: "success",
title: "Success",
text: "Configuration updated successfully",
});
})
.catch(async () => {
setTransitionState("error");
await notify({
status: "error",
title:
"Configuration update failed. Ensure fields are filled correctly and you have MANAGE_APPS permission",
});
});
};
const onChange = (event: ChangeEvent) => {
const { name, value } = event.target as HTMLInputElement;
setConfiguration((prev) =>
prev!.map((prevField) => (prevField.key === name ? { ...prevField, value } : prevField))
);
};
if (error) {
console.error("Can't establish connection with the App API: ", error);
return (
<div>
<h1> Can&apos;t connect with the App API</h1>
You may see this error because:
<ul>
<li>Internet connection has been lost</li>
<li>
Application installation process is still in progress. If you use Vercel, you may need
to wait for redeployment of the app - try again in a minute.
</li>
<li>
Application is misconfigured. If you would like to know more how auth configuration is
kept,{" "}
<a
href="https://github.com/saleor/saleor-app-sdk/blob/main/docs/apl.md"
target="_blank"
rel="noreferrer"
>
go to APL documentation
</a>
.
</li>
</ul>
</div>
);
}
if (configuration === undefined) {
return <Skeleton />;
}
return (
<div>
<TitleBar
icon={<AppIcon theme="rgb(58, 86, 199)" text="K" />}
bottomMargin
name="Saleor Klaviyo App"
author="By Saleor Commerce"
/>
<AppColumnsLayout>
<div />
<Section>
<form onSubmit={handleSubmit}>
{configuration!.map(({ key, value }) => (
<div key={key} className={classes.fieldContainer}>
<TextField label={key} name={key} fullWidth onChange={onChange} value={value} />
</div>
))}
<div>
<ConfirmButton
type="submit"
variant="primary"
transitionState={transitionState}
labels={{
confirm: "Save",
error: "Error",
}}
className={classes.confirmButton}
/>
</div>
</form>
</Section>
<Instructions />
</AppColumnsLayout>
</div>
);
}
export default withAuthorization({
notIframe: <AccessWarning cause="not_in_iframe" />,
unmounted: null,
noDashboardToken: <AccessWarning cause="missing_access_token" />,
dashboardTokenInvalid: <AccessWarning cause="invalid_access_token" />,
})(Configuration);