Matomo integration (#7009)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "google",
|
||||
"name": "Google",
|
||||
"priority": 10,
|
||||
"priority": 0,
|
||||
"description": "Adds a Google authentication provider."
|
||||
}
|
||||
|
||||
26
plugins/googleanalytics/client/Icon.tsx
Normal file
26
plugins/googleanalytics/client/Icon.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
/** The size of the icon, 24px is default to match standard icons */
|
||||
size?: number;
|
||||
/** The color of the icon, defaults to the current text color */
|
||||
fill?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function GoogleLogo({ size = 24, fill = "currentColor", className }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path d="M19.2312 10.5455H11.8276V13.6364H16.0892C15.6919 15.6 14.0306 16.7273 11.8276 16.7273C9.22733 16.7273 7.13267 14.6182 7.13267 12C7.13267 9.38182 9.22733 7.27273 11.8276 7.27273C12.9472 7.27273 13.9584 7.67273 14.7529 8.32727L17.0643 6C15.6558 4.76364 13.85 4 11.8276 4C7.42159 4 3.88232 7.56364 3.88232 12C3.88232 16.4364 7.42159 20 11.8276 20C15.8002 20 19.4117 17.0909 19.4117 12C19.4117 11.5273 19.3395 11.0182 19.2312 10.5455Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleLogo;
|
||||
107
plugins/googleanalytics/client/Settings.tsx
Normal file
107
plugins/googleanalytics/client/Settings.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import find from "lodash/find";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
import SettingRow from "~/scenes/Settings/components/SettingRow";
|
||||
import Button from "~/components/Button";
|
||||
import Heading from "~/components/Heading";
|
||||
import GoogleIcon from "~/components/Icons/GoogleIcon";
|
||||
import Input from "~/components/Input";
|
||||
import Scene from "~/components/Scene";
|
||||
import Text from "~/components/Text";
|
||||
import useStores from "~/hooks/useStores";
|
||||
|
||||
type FormData = {
|
||||
measurementId: string;
|
||||
};
|
||||
|
||||
function GoogleAnalytics() {
|
||||
const { integrations } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const integration = find(integrations.orderedData, {
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.GoogleAnalytics,
|
||||
}) as Integration<IntegrationType.Analytics> | undefined;
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit: formHandleSubmit,
|
||||
formState,
|
||||
} = useForm<FormData>({
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
measurementId: integration?.settings.measurementId,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Analytics,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({ measurementId: integration?.settings.measurementId });
|
||||
}, [integration, reset]);
|
||||
|
||||
const handleSubmit = React.useCallback(
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
if (data.measurementId) {
|
||||
await integrations.save({
|
||||
id: integration?.id,
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.GoogleAnalytics,
|
||||
settings: {
|
||||
measurementId: data.measurementId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await integration?.delete();
|
||||
}
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<Scene title={t("Google Analytics")} icon={<GoogleIcon />}>
|
||||
<Heading>{t("Google Analytics")}</Heading>
|
||||
|
||||
<Text as="p" type="secondary">
|
||||
<Trans>
|
||||
Add a Google Analytics 4 measurement ID to send document views and
|
||||
analytics from the workspace to your own Google Analytics account.
|
||||
</Trans>
|
||||
</Text>
|
||||
<form onSubmit={formHandleSubmit(handleSubmit)}>
|
||||
<SettingRow
|
||||
label={t("Measurement ID")}
|
||||
name="measurementId"
|
||||
description={t(
|
||||
'Create a "Web" stream in your Google Analytics admin dashboard and copy the measurement ID from the generated code snippet to install.'
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input placeholder="G-XXXXXXXXX1" {...register("measurementId")} />
|
||||
</SettingRow>
|
||||
|
||||
<Button type="submit" disabled={formState.isSubmitting}>
|
||||
{formState.isSubmitting ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(GoogleAnalytics);
|
||||
6
plugins/googleanalytics/plugin.json
Normal file
6
plugins/googleanalytics/plugin.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": "googleanalytics",
|
||||
"name": "Google Analytics",
|
||||
"priority": 30,
|
||||
"description": "Adds support for reporting analytics to a Google."
|
||||
}
|
||||
22
plugins/matomo/client/Icon.tsx
Normal file
22
plugins/matomo/client/Icon.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
/** The size of the icon, 24px is default to match standard icons */
|
||||
size?: number;
|
||||
/** The color of the icon, defaults to the current text color */
|
||||
fill?: string;
|
||||
};
|
||||
|
||||
export default function Icon({ size = 24, fill = "currentColor" }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
>
|
||||
<path d="M15.7624 15.309L18.2171 11.588C18.5688 11.0797 18.7702 10.4684 18.7702 9.81395C18.7702 9.72425 18.7668 9.63787 18.76 9.5515L21.4981 13.6113C21.5084 13.6246 21.5118 13.6346 21.522 13.6478L21.5732 13.7243L21.5698 13.7276C21.8395 14.1561 21.9966 14.6578 22 15.1927C22 16.7409 20.7095 17.9967 19.1185 17.9967C18.142 17.9967 17.2851 17.5249 16.7627 16.804L16.7593 16.8073L16.7422 16.7807C16.7286 16.7608 16.7149 16.7375 16.6979 16.7176M15.5336 14.9635L17.1041 12.5814C15.1767 13.6317 12.5053 12.4636 12.2253 10.1993C12.2253 10.1927 12.2253 10.186 12.2219 10.1794L10.8938 8.35216H10.8904C10.3817 7.5515 9.47695 7.01661 8.43564 7.01661C8.43223 7.01661 8.43223 7.01661 8.42882 7.01661C8.4254 7.01661 8.4254 7.01661 8.42199 7.01661C7.38409 7.01661 6.47593 7.5515 5.97064 8.35216H5.96722L3.29737 12.4086C3.76852 12.1528 4.30795 12.01 4.88153 12.01C6.55446 12.01 7.93718 13.2359 8.13179 14.8106L9.63059 16.8571H9.63401C10.1564 17.5482 10.9997 18 11.9522 18H11.959H11.9659C12.9184 18 13.7583 17.5515 14.2841 16.8571H14.2875L14.3114 16.8206C14.3694 16.7409 14.424 16.6578 14.4787 16.5681L15.5336 14.9668V14.9635ZM12.6009 9.80731C12.6009 11.3588 13.8914 12.6146 15.4858 12.6146C17.0802 12.6146 18.3708 11.3588 18.3708 9.80731C18.3708 8.25581 17.0802 7 15.4858 7C13.8914 7 12.6009 8.25581 12.6009 9.80731ZM2 15.196C2 16.7442 3.29054 18 4.88153 18C6.47252 18 7.76306 16.7442 7.76306 15.196C7.76306 13.6478 6.47252 12.392 4.88153 12.392C3.29054 12.392 2 13.6478 2 15.196Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
128
plugins/matomo/client/Settings.tsx
Normal file
128
plugins/matomo/client/Settings.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import find from "lodash/find";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
import SettingRow from "~/scenes/Settings/components/SettingRow";
|
||||
import Button from "~/components/Button";
|
||||
import Heading from "~/components/Heading";
|
||||
import Input from "~/components/Input";
|
||||
import Scene from "~/components/Scene";
|
||||
import Text from "~/components/Text";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import Icon from "./Icon";
|
||||
|
||||
type FormData = {
|
||||
instanceUrl: string;
|
||||
measurementId: string;
|
||||
};
|
||||
|
||||
function Matomo() {
|
||||
const { integrations } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const integration = find(integrations.orderedData, {
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Matomo,
|
||||
}) as Integration<IntegrationType.Analytics> | undefined;
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit: formHandleSubmit,
|
||||
formState,
|
||||
} = useForm<FormData>({
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
instanceUrl: integration?.settings.instanceUrl,
|
||||
measurementId: integration?.settings.measurementId,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Analytics,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
measurementId: integration?.settings.measurementId,
|
||||
instanceUrl: integration?.settings.instanceUrl,
|
||||
});
|
||||
}, [integration, reset]);
|
||||
|
||||
const handleSubmit = React.useCallback(
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
if (data.instanceUrl && data.measurementId) {
|
||||
await integrations.save({
|
||||
id: integration?.id,
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Matomo,
|
||||
settings: {
|
||||
measurementId: data.measurementId,
|
||||
// Ensure the URL ends with a trailing slash
|
||||
instanceUrl: data.instanceUrl.replace(/\/?$/, "/"),
|
||||
} as Integration<IntegrationType.Analytics>["settings"],
|
||||
});
|
||||
} else {
|
||||
await integration?.delete();
|
||||
}
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<Scene title="Matomo" icon={<Icon />}>
|
||||
<Heading>Matomo</Heading>
|
||||
|
||||
<Text as="p" type="secondary">
|
||||
<Trans>
|
||||
Configure a Matomo installation to send views and analytics from the
|
||||
workspace to your own Matomo instance.
|
||||
</Trans>
|
||||
</Text>
|
||||
<form onSubmit={formHandleSubmit(handleSubmit)}>
|
||||
<SettingRow
|
||||
label={t("Instance URL")}
|
||||
name="instanceUrl"
|
||||
description={t(
|
||||
"The URL of your Matomo instance. If you are using Matomo Cloud it will end in matomo.cloud/"
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input
|
||||
required
|
||||
placeholder="https://instance.matomo.cloud/"
|
||||
{...register("instanceUrl")}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
label={t("Site ID")}
|
||||
name="measurementId"
|
||||
description={t(
|
||||
"An ID that uniquely identifies the website in your Matomo instance."
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input required placeholder="1" {...register("measurementId")} />
|
||||
</SettingRow>
|
||||
|
||||
<Button type="submit" disabled={formState.isSubmitting}>
|
||||
{formState.isSubmitting ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(Matomo);
|
||||
8
plugins/matomo/plugin.json
Normal file
8
plugins/matomo/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "matomo",
|
||||
"name": "Matomo",
|
||||
"roles": ["admin"],
|
||||
"priority": 40,
|
||||
"description": "Adds support for reporting analytics to a Matomo server.",
|
||||
"deployments": ["community", "enterprise"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "oidc",
|
||||
"name": "OIDC",
|
||||
"priority": 30,
|
||||
"priority": 10,
|
||||
"description": "Adds an OpenID compatible authentication provider."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "slack",
|
||||
"name": "Slack",
|
||||
"priority": 40,
|
||||
"priority": 20,
|
||||
"roles": ["admin", "member"],
|
||||
"description": "Adds a Slack authentication provider, support for the /outline slash command, and link unfurling."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "webhooks",
|
||||
"name": "Webhooks",
|
||||
"priority": 200,
|
||||
"priority": 0,
|
||||
"description": "Adds HTTP webhooks for various events."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user