Compare commits

...

1 Commits

Author SHA1 Message Date
Tom Moor dac8843934 fix: Non-integration plugins missing in settings
Other minor refactors
2025-05-10 12:39:59 -04:00
10 changed files with 44 additions and 51 deletions
+9 -6
View File
@@ -23,18 +23,21 @@ import ToggleButton from "./components/ToggleButton";
import Version from "./components/Version";
function SettingsSidebar() {
const { ui } = useStores();
const { ui, integrations } = useStores();
const { t } = useTranslation();
const history = useHistory();
const location = useLocation();
let configs = useSettingsConfig();
const configs = useSettingsConfig();
configs = configs.filter((item) =>
"isActive" in item ? item.isActive : true
const groupedConfig = groupBy(
configs.filter((item) =>
item.group === "Integrations" && item.pluginId
? integrations.findByService(item.pluginId)
: true
),
"group"
);
const groupedConfig = groupBy(configs, "group");
const returnToApp = React.useCallback(() => {
history.push("/home");
}, [history]);
+1 -2
View File
@@ -18,8 +18,7 @@ export default function useEmbeds(loadIfMissing = false) {
React.useEffect(() => {
async function fetchEmbedIntegrations() {
try {
await integrations.fetchPage({
limit: 100,
await integrations.fetchAll({
type: IntegrationType.Embed,
});
} catch (err) {
+2 -5
View File
@@ -54,7 +54,7 @@ export type ConfigItem = {
preload?: () => void;
enabled: boolean;
group: string;
isActive?: boolean;
pluginId?: string;
};
const useSettingsConfig = () => {
@@ -231,6 +231,7 @@ const useSettingsConfig = () => {
? integrationSettingsPath(plugin.id)
: settingsPath(plugin.id),
group: t(group),
pluginId: plugin.id,
description: plugin.value.description,
component: plugin.value.component.Component,
preload: plugin.value.component.preload,
@@ -238,10 +239,6 @@ const useSettingsConfig = () => {
? plugin.value.enabled(team, user)
: can.update,
icon: plugin.value.icon,
isActive: integrations.orderedData.some(
(integration) =>
integration.service.toLowerCase() === plugin.id.toLowerCase()
),
} as ConfigItem);
});
+19 -9
View File
@@ -1,3 +1,4 @@
import groupBy from "lodash/groupBy";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import styled from "styled-components";
@@ -7,28 +8,34 @@ import InputSearch from "~/components/InputSearch";
import Scene from "~/components/Scene";
import Text from "~/components/Text";
import useSettingsConfig from "~/hooks/useSettingsConfig";
import useStores from "~/hooks/useStores";
import { settingsPath } from "~/utils/routeHelpers";
import IntegrationCard from "./components/IntegrationCard";
import { StickyFilters } from "./components/StickyFilters";
export function Integrations() {
const { t } = useTranslation();
let items = useSettingsConfig();
const { integrations } = useStores();
const items = useSettingsConfig();
const [query, setQuery] = React.useState("");
const handleQuery = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value);
};
items = items
.filter(
const groupedItems = groupBy(
items.filter(
(item) =>
item.group === "Integrations" &&
item.enabled &&
item.path !== settingsPath("integrations") &&
item.name.toLowerCase().includes(query.toLowerCase())
)
.sort((item) => (item.isActive ? -1 : 1));
),
(item) =>
item.pluginId && integrations.findByService(item.pluginId)
? "connected"
: "available"
);
return (
<Scene title={t("Integrations")}>
@@ -47,16 +54,19 @@ export function Integrations() {
/>
</StickyFilters>
<CardsFlex gap={30} wrap>
{items.map((item) => (
<Cards gap={30} wrap>
{groupedItems.connected.map((item) => (
<IntegrationCard key={item.path} integration={item} isConnected />
))}
{groupedItems.available.map((item) => (
<IntegrationCard key={item.path} integration={item} />
))}
</CardsFlex>
</Cards>
</Scene>
);
}
const CardsFlex = styled(Flex)`
const Cards = styled(Flex)`
margin-top: 20px;
width: "100%";
`;
@@ -10,20 +10,22 @@ import Text from "../../../components/Text";
type Props = {
integration: ConfigItem;
isConnected?: boolean;
};
function IntegrationCard({ integration }: Props) {
function IntegrationCard({ integration, isConnected }: Props) {
const { t } = useTranslation();
return (
<Card as={Link} to={integration.path}>
<Flex align="center" gap={8}>
<integration.icon size={48} />
<Flex auto column>
<Name>{integration.name}</Name>
{integration.isActive && <Status>{t("Connected")}</Status>}
{isConnected && <Status>{t("Connected")}</Status>}
</Flex>
<Button as={Link} to={integration.path} neutral>
{integration.isActive ? t("Configure") : t("Connect")}
<Button as="span" neutral>
{isConnected ? t("Configure") : t("Connect")}
</Button>
</Flex>
+6
View File
@@ -10,6 +10,12 @@ class IntegrationsStore extends Store<Integration> {
super(rootStore, Integration);
}
findByService(service: string) {
return this.orderedData.find(
(integration) => integration.service === service
);
}
@computed
get orderedData(): Integration[] {
return naturalSort(Array.from(this.data.values()), "name");
@@ -40,12 +40,6 @@ function GoogleAnalytics() {
},
});
React.useEffect(() => {
void integrations.fetchPage({
type: IntegrationType.Analytics,
});
}, [integrations]);
React.useEffect(() => {
reset({ measurementId: integration?.settings.measurementId });
}, [integration, reset]);
-6
View File
@@ -42,12 +42,6 @@ function Matomo() {
},
});
React.useEffect(() => {
void integrations.fetchPage({
type: IntegrationType.Analytics,
});
}, [integrations]);
React.useEffect(() => {
reset({
measurementId: integration?.settings.measurementId,
+1 -7
View File
@@ -34,13 +34,7 @@ function Slack() {
const error = query.get("error");
React.useEffect(() => {
void collections.fetchPage({
limit: 100,
});
void integrations.fetchPage({
service: IntegrationService.Slack,
limit: 100,
});
void collections.fetchAll();
}, [collections, integrations]);
const commandIntegration = integrations.find({
-6
View File
@@ -44,12 +44,6 @@ function Umami() {
},
});
React.useEffect(() => {
void integrations.fetchPage({
type: IntegrationType.Analytics,
});
}, [integrations]);
React.useEffect(() => {
reset({
umamiWebsiteId: integration?.settings.measurementId,