mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-15 03:52:13 +01:00
🚧 WIP on integration menu
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
"createItem": "+ create {{item}}",
|
||||
"sections": {
|
||||
"settings": "Settings",
|
||||
"integrations": "Integrations",
|
||||
"dangerZone": "Danger zone"
|
||||
},
|
||||
"secrets": {
|
||||
|
||||
4
public/locales/en/settings/integrations.json
Normal file
4
public/locales/en/settings/integrations.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Integrations settings",
|
||||
"description": "Here you can configure all the integrations you want to use within Honmarr"
|
||||
}
|
||||
138
src/components/Config/Integration/IntegrationModal.tsx
Normal file
138
src/components/Config/Integration/IntegrationModal.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { Accordion, Modal, Stack, Text, Title, rem } from '@mantine/core';
|
||||
import { Form, useForm } from '@mantine/form';
|
||||
import Image from 'next/image';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { integrationsList } from '~/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector';
|
||||
import {
|
||||
IntegrationOptionsRenderer,
|
||||
IntegrationOptionsRendererNoForm,
|
||||
} from '~/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { AppIntegrationType, AppType, IntegrationType } from '~/types/app';
|
||||
|
||||
const ModalTitle = ({ title, description }: { title: string; description: string }) => (
|
||||
<div>
|
||||
<Title order={3} style={{ marginBottom: 0 }}>
|
||||
{title}
|
||||
</Title>
|
||||
<Text color="dimmed">{description}</Text>
|
||||
</div>
|
||||
);
|
||||
|
||||
function IntegrationDisplay({ integration }: { integration: AppIntegrationType }) {
|
||||
if (!integration.type) return null;
|
||||
return (
|
||||
<Accordion.Item value={integration.id}>
|
||||
<Accordion.Control>{integration.name}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<IntegrationOptionsRendererNoForm
|
||||
type={integration.type}
|
||||
properties={integration.properties}
|
||||
/>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
}
|
||||
|
||||
interface IntegrationGroupedType {
|
||||
type: IntegrationType;
|
||||
integration: AppIntegrationType[];
|
||||
}
|
||||
|
||||
// export type IntegrationType =
|
||||
// | 'readarr'
|
||||
// | 'radarr'
|
||||
// | 'sonarr'
|
||||
// | 'lidarr'
|
||||
// | 'sabnzbd'
|
||||
// | 'jellyseerr'
|
||||
// | 'overseerr'
|
||||
// | 'deluge'
|
||||
// | 'qBittorrent'
|
||||
// | 'transmission'
|
||||
// | 'plex'
|
||||
// | 'jellyfin'
|
||||
// | 'nzbGet'
|
||||
// | 'pihole'
|
||||
// | 'adGuardHome';
|
||||
export function IntegrationsAccordion() {
|
||||
const { config } = useConfigContext();
|
||||
|
||||
console.log(config.integrations);
|
||||
if (!config.integrations) {
|
||||
config.integrations = [];
|
||||
}
|
||||
|
||||
// Fill configIntegrationList with config.integrations in the
|
||||
const configIntegrationList: IntegrationGroupedType[] = [];
|
||||
config.integrations.forEach((configIntegration) => {
|
||||
const existingIntegration = configIntegrationList.find(
|
||||
(integration) => integration.type === configIntegration.type
|
||||
);
|
||||
if (existingIntegration) {
|
||||
existingIntegration.integration.push(configIntegration);
|
||||
} else {
|
||||
configIntegrationList.push({
|
||||
type: configIntegration.type!,
|
||||
integration: [configIntegration],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Accordion variant="separated" multiple>
|
||||
{configIntegrationList.map((configIntegration) => {
|
||||
// Match configIntegration with integrationsList
|
||||
const integration = integrationsList.find(
|
||||
(integration) => integration.value === configIntegration.type
|
||||
);
|
||||
if (!integration) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Accordion.Item value={integration.label ?? integration.value} key={integration.value}>
|
||||
<Accordion.Control
|
||||
icon={
|
||||
<Image
|
||||
src={integration.image}
|
||||
width={24}
|
||||
height={24}
|
||||
alt={integration.label ?? integration.value}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{integration.label ?? integration.value}
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Accordion variant="contained" radius="md" multiple>
|
||||
{configIntegration.integration.map((item) => (
|
||||
<IntegrationDisplay integration={item} />
|
||||
))}
|
||||
</Accordion>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
})}
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
|
||||
export function IntegrationModal({
|
||||
opened,
|
||||
closeModal,
|
||||
}: {
|
||||
opened: boolean;
|
||||
closeModal: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation('settings/integrations');
|
||||
return (
|
||||
<Modal
|
||||
title={<ModalTitle title={t('title')} description={t('description')} />}
|
||||
opened={opened}
|
||||
onClose={() => closeModal()}
|
||||
size={rem(1000)}
|
||||
>
|
||||
<IntegrationsAccordion />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -106,6 +106,7 @@ export const GenericSecretInput = ({
|
||||
{displayUpdateField === true ? (
|
||||
<PasswordInput
|
||||
required
|
||||
autoComplete={`homarr-${type}-password`}
|
||||
defaultValue={value}
|
||||
placeholder="new secret"
|
||||
styles={{ root: { width: 200 } }}
|
||||
|
||||
@@ -16,86 +16,88 @@ interface IntegrationSelectorProps {
|
||||
form: UseFormReturnType<AppType, (item: AppType) => AppType>;
|
||||
}
|
||||
|
||||
export const integrationsList: SelectItem[] = [
|
||||
{
|
||||
value: 'sabnzbd',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/sabnzbd.svg',
|
||||
label: 'SABnzbd',
|
||||
},
|
||||
{
|
||||
value: 'nzbGet',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png',
|
||||
label: 'NZBGet',
|
||||
},
|
||||
{
|
||||
value: 'deluge',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/deluge.svg',
|
||||
label: 'Deluge',
|
||||
},
|
||||
{
|
||||
value: 'transmission',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/transmission.svg',
|
||||
label: 'Transmission',
|
||||
},
|
||||
{
|
||||
value: 'qBittorrent',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/qbittorrent.svg',
|
||||
label: 'qBittorrent',
|
||||
},
|
||||
{
|
||||
value: 'jellyseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/jellyseerr.svg',
|
||||
label: 'Jellyseerr',
|
||||
},
|
||||
{
|
||||
value: 'overseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/overseerr.svg',
|
||||
label: 'Overseerr',
|
||||
},
|
||||
{
|
||||
value: 'sonarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/sonarr.svg',
|
||||
label: 'Sonarr',
|
||||
},
|
||||
{
|
||||
value: 'radarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/radarr.svg',
|
||||
label: 'Radarr',
|
||||
},
|
||||
{
|
||||
value: 'lidarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/lidarr.svg',
|
||||
label: 'Lidarr',
|
||||
},
|
||||
{
|
||||
value: 'readarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/readarr.svg',
|
||||
label: 'Readarr',
|
||||
},
|
||||
{
|
||||
value: 'jellyfin',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/jellyfin.svg',
|
||||
label: 'Jellyfin',
|
||||
},
|
||||
{
|
||||
value: 'plex',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/plex.svg',
|
||||
label: 'Plex',
|
||||
},
|
||||
{
|
||||
value: 'pihole',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/pi-hole.svg',
|
||||
label: 'PiHole',
|
||||
},
|
||||
{
|
||||
value: 'adGuardHome',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/adguard-home.svg',
|
||||
label: 'AdGuard Home',
|
||||
},
|
||||
];
|
||||
|
||||
export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
|
||||
const data: SelectItem[] = [
|
||||
{
|
||||
value: 'sabnzbd',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png',
|
||||
label: 'SABnzbd',
|
||||
},
|
||||
{
|
||||
value: 'nzbGet',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png',
|
||||
label: 'NZBGet',
|
||||
},
|
||||
{
|
||||
value: 'deluge',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png',
|
||||
label: 'Deluge',
|
||||
},
|
||||
{
|
||||
value: 'transmission',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png',
|
||||
label: 'Transmission',
|
||||
},
|
||||
{
|
||||
value: 'qBittorrent',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png',
|
||||
label: 'qBittorrent',
|
||||
},
|
||||
{
|
||||
value: 'jellyseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png',
|
||||
label: 'Jellyseerr',
|
||||
},
|
||||
{
|
||||
value: 'overseerr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png',
|
||||
label: 'Overseerr',
|
||||
},
|
||||
{
|
||||
value: 'sonarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png',
|
||||
label: 'Sonarr',
|
||||
},
|
||||
{
|
||||
value: 'radarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png',
|
||||
label: 'Radarr',
|
||||
},
|
||||
{
|
||||
value: 'lidarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png',
|
||||
label: 'Lidarr',
|
||||
},
|
||||
{
|
||||
value: 'readarr',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png',
|
||||
label: 'Readarr',
|
||||
},
|
||||
{
|
||||
value: 'jellyfin',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png',
|
||||
label: 'Jellyfin',
|
||||
},
|
||||
{
|
||||
value: 'plex',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png',
|
||||
label: 'Plex',
|
||||
},
|
||||
{
|
||||
value: 'pihole',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png',
|
||||
label: 'PiHole',
|
||||
},
|
||||
{
|
||||
value: 'adGuardHome',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png',
|
||||
label: 'AdGuard Home',
|
||||
},
|
||||
].filter((x) => Object.keys(integrationFieldProperties).includes(x.value));
|
||||
const data = integrationsList.filter((x) => Object.keys(integrationFieldProperties).includes(x.value));
|
||||
|
||||
const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => {
|
||||
if (!value) return [];
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Stack } from '@mantine/core';
|
||||
import { Accordion, Stack } from '@mantine/core';
|
||||
import { UseFormReturnType } from '@mantine/form';
|
||||
import { IconKey } from '@tabler/icons-react';
|
||||
|
||||
import {
|
||||
IntegrationField,
|
||||
integrationFieldDefinitions,
|
||||
integrationFieldProperties,
|
||||
AppIntegrationPropertyType,
|
||||
AppType,
|
||||
IntegrationField,
|
||||
IntegrationType,
|
||||
integrationFieldDefinitions,
|
||||
integrationFieldProperties,
|
||||
} from '../../../../../../../../types/app';
|
||||
import { GenericSecretInput } from '../InputElements/GenericSecretInput';
|
||||
|
||||
@@ -14,6 +16,85 @@ interface IntegrationOptionsRendererProps {
|
||||
form: UseFormReturnType<AppType, (values: AppType) => AppType>;
|
||||
}
|
||||
|
||||
export const IntegrationOptionsRendererNoForm = ({
|
||||
type,
|
||||
properties,
|
||||
}: {
|
||||
type: IntegrationType;
|
||||
properties: any;
|
||||
}) => {
|
||||
const selectedIntegration = type;
|
||||
if (!selectedIntegration) return null;
|
||||
|
||||
const displayedProperties = integrationFieldProperties[type];
|
||||
|
||||
return (
|
||||
<Stack spacing="xs" mb="md">
|
||||
{displayedProperties.map((property, index) => {
|
||||
const [_, definition] = Object.entries(integrationFieldDefinitions).find(
|
||||
([key]) => property === key
|
||||
)!;
|
||||
|
||||
let indexInFormValue = properties.findIndex((p: any) => p.field === property) ?? -1;
|
||||
if (indexInFormValue === -1) {
|
||||
const { type } = Object.entries(integrationFieldDefinitions).find(
|
||||
([k, v]) => k === property
|
||||
)![1];
|
||||
const newProperty: AppIntegrationPropertyType = {
|
||||
type,
|
||||
field: property as IntegrationField,
|
||||
isDefined: false,
|
||||
};
|
||||
indexInFormValue = properties.length;
|
||||
}
|
||||
const formValue = properties[indexInFormValue];
|
||||
|
||||
const isPresent = formValue?.isDefined;
|
||||
const accessabilityType = formValue?.type;
|
||||
|
||||
if (!definition) {
|
||||
return (
|
||||
<GenericSecretInput
|
||||
onClickUpdateButton={(value) => {
|
||||
// form.setFieldValue(`integration.properties.${index}.value`, value);
|
||||
// form.setFieldValue(
|
||||
// `integration.properties.${index}.isDefined`,
|
||||
// value !== undefined
|
||||
// );
|
||||
//TODO: Add a super form to handle saving of the properties
|
||||
}}
|
||||
key={`input-${property}`}
|
||||
label={`${property} (potentionally unmapped)`}
|
||||
secretIsPresent={isPresent}
|
||||
setIcon={IconKey}
|
||||
type={accessabilityType}
|
||||
value={formValue}
|
||||
// {...form.getInputProps(`integration.properties.${index}.value`)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GenericSecretInput
|
||||
onClickUpdateButton={(value) => {
|
||||
// form.setFieldValue(`integration.properties.${index}.value`, value);
|
||||
// form.setFieldValue(`integration.properties.${index}.isDefined`, value !== undefined);
|
||||
// TODO: THIS
|
||||
}}
|
||||
key={`input-${definition.label}`}
|
||||
label={definition.label}
|
||||
secretIsPresent={isPresent}
|
||||
setIcon={definition.icon}
|
||||
type={accessabilityType}
|
||||
value={formValue}
|
||||
// {...form.getInputProps(`integration.properties.${index}.value`)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererProps) => {
|
||||
const selectedIntegration = form.values.integration?.type;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { motion } from 'framer-motion';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ReactNode } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useConfigContext } from '../../../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../../../config/store';
|
||||
import { openContextModalGeneric } from '../../../../../../tools/mantineModalManagerExtensions';
|
||||
@@ -113,6 +114,8 @@ export const AvailableElementTypes = ({
|
||||
integration: {
|
||||
type: null,
|
||||
properties: [],
|
||||
id: uuidv4(),
|
||||
name: 'New integration',
|
||||
},
|
||||
},
|
||||
allowAppNamePropagation: true,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export function IntegrationPanel() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Integration Panel</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { Badge, Button, Menu } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconInfoCircle, IconMenu2, IconSettings } from '@tabler/icons-react';
|
||||
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
||||
import {
|
||||
IconInfoCircle,
|
||||
IconMenu2,
|
||||
IconPlug,
|
||||
IconPlugConnected,
|
||||
IconSettings,
|
||||
} from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { IntegrationModal } from '~/components/Config/Integration/IntegrationModal';
|
||||
|
||||
import { useEditModeInformationStore } from '../../../hooks/useEditModeInformation';
|
||||
import { AboutModal } from '../../Dashboard/Modals/AboutModal/AboutModal';
|
||||
import { SettingsDrawer } from '../../Settings/SettingsDrawer';
|
||||
@@ -12,9 +20,11 @@ import { EditModeToggle } from './SettingsMenu/EditModeToggle';
|
||||
export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) {
|
||||
const [drawerOpened, drawer] = useDisclosure(false);
|
||||
const { t } = useTranslation('common');
|
||||
const [integrationsModalOpened, integrationsModal] = useDisclosure(false);
|
||||
const [aboutModalOpened, aboutModal] = useDisclosure(false);
|
||||
const { classes } = useCardStyles(true);
|
||||
const { editModeEnabled } = useEditModeInformationStore();
|
||||
useHotkeys([['mod+o', () => integrationsModal.toggle()]]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -33,6 +43,12 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str
|
||||
{t('sections.settings')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
icon={<IconPlugConnected strokeWidth={1.2} size={18} />}
|
||||
onClick={integrationsModal.open}
|
||||
>
|
||||
{t('sections.integrations')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<IconInfoCircle strokeWidth={1.2} size={18} />}
|
||||
rightSection={
|
||||
@@ -53,6 +69,7 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str
|
||||
closeDrawer={drawer.close}
|
||||
newVersionAvailable={newVersionAvailable}
|
||||
/>
|
||||
<IntegrationModal opened={integrationsModalOpened} closeModal={integrationsModal.close} />
|
||||
<AboutModal
|
||||
opened={aboutModalOpened}
|
||||
closeModal={aboutModal.close}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ConfigType } from '../types/config';
|
||||
import { useConfigStore } from './store';
|
||||
|
||||
export type ConfigContextType = {
|
||||
config: ConfigType | undefined;
|
||||
config: ConfigType;
|
||||
name: string | undefined;
|
||||
configVersion: number | undefined;
|
||||
increaseVersion: () => void;
|
||||
@@ -14,7 +14,7 @@ export type ConfigContextType = {
|
||||
|
||||
const ConfigContext = createContext<ConfigContextType>({
|
||||
name: 'unknown',
|
||||
config: undefined,
|
||||
config: {} as ConfigType,
|
||||
configVersion: undefined,
|
||||
increaseVersion: () => {},
|
||||
setConfigName: () => {},
|
||||
@@ -38,7 +38,7 @@ export const ConfigProvider = ({ children }: { children: ReactNode }) => {
|
||||
<ConfigContext.Provider
|
||||
value={{
|
||||
name: configName,
|
||||
config: currentConfig,
|
||||
config: currentConfig!,
|
||||
configVersion,
|
||||
increaseVersion: () => setConfigVersion((v) => v + 1),
|
||||
setConfigName: (name: string) => setConfigName(name),
|
||||
|
||||
@@ -8,6 +8,7 @@ export const dashboardNamespaces = [
|
||||
'layout/header/actions/toggle-edit-mode',
|
||||
'layout/mobile/drawer',
|
||||
'settings/common',
|
||||
'settings/integrations',
|
||||
'settings/general/theme-selector',
|
||||
'settings/general/config-changer',
|
||||
'settings/general/internationalization',
|
||||
|
||||
@@ -50,6 +50,8 @@ export type IntegrationType =
|
||||
|
||||
export type AppIntegrationType = {
|
||||
type: IntegrationType | null;
|
||||
id: string;
|
||||
name: string;
|
||||
properties: AppIntegrationPropertyType[];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CategoryType } from './category';
|
||||
import { WrapperType } from './wrapper';
|
||||
import { ConfigAppType, AppType } from './app';
|
||||
import { SettingsType } from './settings';
|
||||
import { IWidget } from '../widgets/widgets';
|
||||
import { AppIntegrationType, AppType, ConfigAppType } from './app';
|
||||
import { CategoryType } from './category';
|
||||
import { SettingsType } from './settings';
|
||||
import { WrapperType } from './wrapper';
|
||||
|
||||
export interface ConfigType {
|
||||
schemaVersion: number;
|
||||
@@ -12,6 +12,7 @@ export interface ConfigType {
|
||||
apps: AppType[];
|
||||
widgets: IWidget<string, any>[];
|
||||
settings: SettingsType;
|
||||
integrations: AppIntegrationType[]
|
||||
}
|
||||
|
||||
export type BackendConfigType = Omit<ConfigType, 'apps'> & {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
@@ -24,7 +24,9 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
"~/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"include": [
|
||||
@@ -37,4 +39,4 @@
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user