mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
@@ -0,0 +1,29 @@
|
||||
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="M12.0056 17.9792C15.5361 17.9792 18.3981 15.1172 18.3981 11.5867C18.3981 8.05618 15.5361 5.19415 12.0056 5.19415C8.4751 5.19415 5.61307 8.05618 5.61307 11.5867C5.61307 15.1172 8.4751 17.9792 12.0056 17.9792Z"
|
||||
fill="none"
|
||||
stroke={fill}
|
||||
strokeWidth="1.1215"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path d="M19.4393 9.8338H4.57159C4.42287 9.8338 4.28024 9.89288 4.17508 9.99804C4.06992 10.1032 4.01084 10.2458 4.01084 10.3945V10.9665C4.00449 11.1007 4 11.2323 4 11.3665C4 15.7848 7.58168 19.3665 12 19.3665C16.3514 19.3665 19.8916 15.8921 19.9974 11.5658C19.9974 11.5493 20 11.5329 20 11.516V10.3945C20 10.2458 19.9409 10.1032 19.8358 9.99804C19.7306 9.89288 19.588 9.8338 19.4393 9.8338Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
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 = {
|
||||
umamiDomain: string;
|
||||
umamiScriptName: string;
|
||||
umamiWebsiteId: string;
|
||||
};
|
||||
|
||||
function Umami() {
|
||||
const { integrations } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const integration = find(integrations.orderedData, {
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Umami,
|
||||
}) as Integration<IntegrationType.Analytics> | undefined;
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit: formHandleSubmit,
|
||||
formState,
|
||||
} = useForm<FormData>({
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
umamiDomain: integration?.settings.instanceUrl,
|
||||
umamiScriptName: integration?.settings.scriptName,
|
||||
umamiWebsiteId: integration?.settings.measurementId,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Analytics,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
umamiWebsiteId: integration?.settings.measurementId,
|
||||
umamiDomain: integration?.settings.instanceUrl,
|
||||
umamiScriptName: integration?.settings.scriptName,
|
||||
});
|
||||
}, [integration, reset]);
|
||||
|
||||
const handleSubmit = React.useCallback(
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
if (data.umamiDomain && data.umamiScriptName && data.umamiWebsiteId) {
|
||||
await integrations.save({
|
||||
id: integration?.id,
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Umami,
|
||||
settings: {
|
||||
measurementId: data.umamiWebsiteId,
|
||||
instanceUrl: data.umamiDomain.replace(/\/?$/, "/"),
|
||||
scriptName: data.umamiScriptName,
|
||||
} 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="Umami" icon={<Icon />}>
|
||||
<Heading>Umami</Heading>
|
||||
|
||||
<Text as="p" type="secondary">
|
||||
<Trans>
|
||||
Configure a Umami installation to send views and analytics from the
|
||||
workspace to your own Umami instance.
|
||||
</Trans>
|
||||
</Text>
|
||||
<form onSubmit={formHandleSubmit(handleSubmit)}>
|
||||
<SettingRow
|
||||
label={t("Instance URL")}
|
||||
name="umamiDomain"
|
||||
description={t(
|
||||
"The URL of your Umami instance. If you are using Umami Cloud it will begin with {{ url }}",
|
||||
{
|
||||
url: "https://cloud.umami.is/",
|
||||
}
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input
|
||||
required
|
||||
placeholder="https://cloud.umami.is/"
|
||||
{...register("umamiDomain")}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
label={t("Script name")}
|
||||
name="umamiScriptName"
|
||||
description={t(
|
||||
"The name of the script file that Umami uses to track analytics."
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input
|
||||
required
|
||||
placeholder="script.js"
|
||||
{...register("umamiScriptName")}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
label={t("Site ID")}
|
||||
name="umamiWebsiteId"
|
||||
description={t(
|
||||
"An ID that uniquely identifies the website in your Umami instance."
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input
|
||||
required
|
||||
placeholder="xxx-xxx-xxx-xxx"
|
||||
{...register("umamiWebsiteId")}
|
||||
/>
|
||||
</SettingRow>
|
||||
|
||||
<Button type="submit" disabled={formState.isSubmitting}>
|
||||
{formState.isSubmitting ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(Umami);
|
||||
@@ -0,0 +1,18 @@
|
||||
import * as React from "react";
|
||||
import { UserRole } from "@shared/types";
|
||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||
import config from "../plugin.json";
|
||||
import Icon from "./Icon";
|
||||
|
||||
PluginManager.add([
|
||||
{
|
||||
...config,
|
||||
type: Hook.Settings,
|
||||
value: {
|
||||
group: "Integrations",
|
||||
icon: Icon,
|
||||
component: React.lazy(() => import("./Settings")),
|
||||
enabled: (_, user) => user.role === UserRole.Admin,
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "umami",
|
||||
"name": "Umami",
|
||||
"priority": 50,
|
||||
"description": "Adds support for reporting analytics to a Umami server.",
|
||||
"deployments": [
|
||||
"community",
|
||||
"enterprise"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user