mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-14 01:15:47 +01:00
💄 Prettier codebase
This commit is contained in:
@@ -7,7 +7,11 @@ export const AccessCustomization = () => {
|
|||||||
const form = useBoardCustomizationFormContext();
|
const form = useBoardCustomizationFormContext();
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Switch label={t('allowGuests.label')} description={t('allowGuests.description')} {...form.getInputProps('access.allowGuests', { type: 'checkbox' })} />
|
<Switch
|
||||||
|
label={t('allowGuests.label')}
|
||||||
|
description={t('allowGuests.description')}
|
||||||
|
{...form.getInputProps('access.allowGuests', { type: 'checkbox' })}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ActionIcon, Space, createStyles } from '@mantine/core';
|
import { ActionIcon, Space, createStyles } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
|
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||||
|
|
||||||
import { MobileRibbonSidebarDrawer } from './MobileRibbonSidebarDrawer';
|
import { MobileRibbonSidebarDrawer } from './MobileRibbonSidebarDrawer';
|
||||||
|
|
||||||
export const MobileRibbons = () => {
|
export const MobileRibbons = () => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SelectItem } from '@mantine/core';
|
import { SelectItem } from '@mantine/core';
|
||||||
import { ContextModalProps, closeModal } from '@mantine/modals';
|
import { ContextModalProps, closeModal } from '@mantine/modals';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { ChangePositionModal } from './ChangePositionModal';
|
import { ChangePositionModal } from './ChangePositionModal';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Button, Flex, Grid, NumberInput, Select, SelectItem } from '@mantine/core';
|
import { Button, Flex, Grid, NumberInput, Select, SelectItem } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
|
||||||
interface ChangePositionModalProps {
|
interface ChangePositionModalProps {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { SelectItem } from '@mantine/core';
|
import { SelectItem } from '@mantine/core';
|
||||||
import { ContextModalProps, closeModal } from '@mantine/modals';
|
import { ContextModalProps, closeModal } from '@mantine/modals';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
|
|
||||||
import widgets from '../../../../widgets';
|
import widgets from '../../../../widgets';
|
||||||
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
||||||
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import {
|
|||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { DebouncedImage } from '../../../IconSelector/DebouncedImage';
|
import { DebouncedImage } from '../../../IconSelector/DebouncedImage';
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { AppearanceTab } from './Tabs/AppereanceTab/AppereanceTab';
|
import { AppearanceTab } from './Tabs/AppereanceTab/AppereanceTab';
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import { UseFormReturnType } from '@mantine/form';
|
|||||||
import { useDebouncedValue } from '@mantine/hooks';
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
|
||||||
import { IconSelector } from '~/components/IconSelector/IconSelector';
|
import { IconSelector } from '~/components/IconSelector/IconSelector';
|
||||||
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
interface AppearanceTabProps {
|
interface AppearanceTabProps {
|
||||||
form: UseFormReturnType<AppType, (values: AppType) => AppType>;
|
form: UseFormReturnType<AppType, (values: AppType) => AppType>;
|
||||||
@@ -83,7 +82,8 @@ export const AppearanceTab = ({
|
|||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
value: 'column',
|
value: 'column',
|
||||||
label: t('appearance.positionAppName.dropdown.top') as string },
|
label: t('appearance.positionAppName.dropdown.top') as string,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'row-reverse',
|
value: 'row-reverse',
|
||||||
label: t('appearance.positionAppName.dropdown.right') as string,
|
label: t('appearance.positionAppName.dropdown.right') as string,
|
||||||
@@ -94,7 +94,8 @@ export const AppearanceTab = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'row',
|
value: 'row',
|
||||||
label: t('appearance.positionAppName.dropdown.left') as string },
|
label: t('appearance.positionAppName.dropdown.left') as string,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
{...form.getInputProps('appearance.positionAppName')}
|
{...form.getInputProps('appearance.positionAppName')}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { Text, TextInput, Tooltip, Stack, Switch, Tabs, Group, useMantineTheme, HoverCard } from '@mantine/core';
|
import {
|
||||||
|
Group,
|
||||||
|
HoverCard,
|
||||||
|
Stack,
|
||||||
|
Switch,
|
||||||
|
Tabs,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Tooltip,
|
||||||
|
useMantineTheme,
|
||||||
|
} from '@mantine/core';
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { InfoCard } from '~/components/InfoCard/InfoCard';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
import { InfoCard } from '~/components/InfoCard/InfoCard'
|
|
||||||
|
|
||||||
interface BehaviourTabProps {
|
interface BehaviourTabProps {
|
||||||
form: UseFormReturnType<AppType, (values: AppType) => AppType>;
|
form: UseFormReturnType<AppType, (values: AppType) => AppType>;
|
||||||
@@ -19,7 +28,7 @@ export const BehaviourTab = ({ form }: BehaviourTabProps) => {
|
|||||||
<Switch
|
<Switch
|
||||||
label={t('behaviour.isOpeningNewTab.label')}
|
label={t('behaviour.isOpeningNewTab.label')}
|
||||||
description={t('behaviour.isOpeningNewTab.description')}
|
description={t('behaviour.isOpeningNewTab.description')}
|
||||||
styles={{ label: { fontWeight: 500, }, description: { marginTop: 0, }, }}
|
styles={{ label: { fontWeight: 500 }, description: { marginTop: 0 } }}
|
||||||
{...form.getInputProps('behaviour.isOpeningNewTab', { type: 'checkbox' })}
|
{...form.getInputProps('behaviour.isOpeningNewTab', { type: 'checkbox' })}
|
||||||
/>
|
/>
|
||||||
<Stack spacing="0.25rem">
|
<Stack spacing="0.25rem">
|
||||||
@@ -27,11 +36,9 @@ export const BehaviourTab = ({ form }: BehaviourTabProps) => {
|
|||||||
<Text size="0.875rem" weight={500}>
|
<Text size="0.875rem" weight={500}>
|
||||||
{t('behaviour.tooltipDescription.label')}
|
{t('behaviour.tooltipDescription.label')}
|
||||||
</Text>
|
</Text>
|
||||||
<InfoCard message={t('behaviour.tooltipDescription.description')}/>
|
<InfoCard message={t('behaviour.tooltipDescription.description')} />
|
||||||
</Group>
|
</Group>
|
||||||
<TextInput
|
<TextInput {...form.getInputProps('behaviour.tooltipDescription')} />
|
||||||
{...form.getInputProps('behaviour.tooltipDescription')}
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Stack, Tabs, Text, TextInput } from '@mantine/core';
|
|||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { IconClick, IconCursorText, IconLink } from '@tabler/icons-react';
|
import { IconClick, IconCursorText, IconLink } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { EditAppModalTab } from '../type';
|
import { EditAppModalTab } from '../type';
|
||||||
|
|
||||||
interface GeneralTabProps {
|
interface GeneralTabProps {
|
||||||
@@ -51,7 +51,7 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
|
|||||||
<Text color="red" mt="sm" size="sm">
|
<Text color="red" mt="sm" size="sm">
|
||||||
{t('behaviour.customProtocolWarning')}
|
{t('behaviour.customProtocolWarning')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
import { Icon } from '@tabler/icons-react';
|
import { Icon } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { AppIntegrationPropertyAccessabilityType } from '~/types/app';
|
import { AppIntegrationPropertyAccessabilityType } from '~/types/app';
|
||||||
|
|
||||||
interface GenericSecretInputProps {
|
interface GenericSecretInputProps {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Group, Image, Select, SelectItem, Text } from '@mantine/core';
|
|||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppIntegrationPropertyType,
|
AppIntegrationPropertyType,
|
||||||
AppIntegrationType,
|
AppIntegrationType,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { IconKey } from '@tabler/icons-react';
|
import { IconKey } from '@tabler/icons-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppIntegrationPropertyType,
|
AppIntegrationPropertyType,
|
||||||
AppType,
|
AppType,
|
||||||
@@ -9,6 +8,7 @@ import {
|
|||||||
integrationFieldDefinitions,
|
integrationFieldDefinitions,
|
||||||
integrationFieldProperties,
|
integrationFieldProperties,
|
||||||
} from '~/types/app';
|
} from '~/types/app';
|
||||||
|
|
||||||
import { GenericSecretInput } from '../InputElements/GenericSecretInput';
|
import { GenericSecretInput } from '../InputElements/GenericSecretInput';
|
||||||
|
|
||||||
interface IntegrationOptionsRendererProps {
|
interface IntegrationOptionsRendererProps {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Alert, Divider, Tabs, Text } from '@mantine/core';
|
|||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { IconAlertTriangle } from '@tabler/icons-react';
|
import { IconAlertTriangle } from '@tabler/icons-react';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { IntegrationSelector } from './Components/InputElements/IntegrationSelector';
|
import { IntegrationSelector } from './Components/InputElements/IntegrationSelector';
|
||||||
import { IntegrationOptionsRenderer } from './Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer';
|
import { IntegrationOptionsRenderer } from './Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { MultiSelect, Stack, Switch, Tabs } from '@mantine/core';
|
import { MultiSelect, Stack, Switch, Tabs } from '@mantine/core';
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { StatusCodes } from '~/tools/acceptableStatusCodes';
|
import { StatusCodes } from '~/tools/acceptableStatusCodes';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ export const NetworkTab = ({ form }: NetworkTabProps) => {
|
|||||||
<Switch
|
<Switch
|
||||||
label={t('network.statusChecker.label')}
|
label={t('network.statusChecker.label')}
|
||||||
description={t('network.statusChecker.description')}
|
description={t('network.statusChecker.description')}
|
||||||
styles={{ label: { fontWeight: 500, }, description: { marginTop: 0, }, }}
|
styles={{ label: { fontWeight: 500 }, description: { marginTop: 0 } }}
|
||||||
defaultChecked={form.values.network.enabledStatusChecker}
|
defaultChecked={form.values.network.enabledStatusChecker}
|
||||||
{...form.getInputProps('network.enabledStatusChecker')}
|
{...form.getInputProps('network.enabledStatusChecker')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { showNotification } from '@mantine/notifications';
|
|||||||
import { Icon, IconChecks } from '@tabler/icons-react';
|
import { Icon, IconChecks } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { IWidget, IWidgetDefinition } from '~/widgets/widgets';
|
import { IWidget, IWidgetDefinition } from '~/widgets/widgets';
|
||||||
|
|
||||||
import { useEditModeStore } from '../../../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../../../Views/useEditModeStore';
|
||||||
import { GenericAvailableElementType } from '../Shared/GenericElementType';
|
import { GenericAvailableElementType } from '../Shared/GenericElementType';
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useConfigContext } from '~/config/provider';
|
|||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { GenericTileMenu } from '../GenericTileMenu';
|
import { GenericTileMenu } from '../GenericTileMenu';
|
||||||
|
|
||||||
interface TileMenuProps {
|
interface TileMenuProps {
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import Consola from 'consola';
|
|||||||
import { TargetAndTransition, Transition, motion } from 'framer-motion';
|
import { TargetAndTransition, Transition, motion } from 'framer-motion';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { RouterOutputs, api } from '~/utils/api';
|
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
import { RouterOutputs, api } from '~/utils/api';
|
||||||
|
|
||||||
interface AppPingProps {
|
interface AppPingProps {
|
||||||
app: AppType;
|
app: AppType;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Affix, Box, Text, Tooltip, UnstyledButton } from '@mantine/core';
|
|||||||
import { createStyles, useMantineTheme } from '@mantine/styles';
|
import { createStyles, useMantineTheme } from '@mantine/styles';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
|
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { HomarrCardWrapper } from '../HomarrCardWrapper';
|
import { HomarrCardWrapper } from '../HomarrCardWrapper';
|
||||||
import { BaseTileProps } from '../type';
|
import { BaseTileProps } from '../type';
|
||||||
@@ -111,7 +111,7 @@ const useStyles = createStyles((theme, _params, getRef) => ({
|
|||||||
overflow: 'visible',
|
overflow: 'visible',
|
||||||
flexGrow: 5,
|
flexGrow: 5,
|
||||||
},
|
},
|
||||||
appImage:{
|
appImage: {
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ActionIcon, Menu } from '@mantine/core';
|
import { ActionIcon, Menu } from '@mantine/core';
|
||||||
import { IconLayoutKanban, IconPencil, IconSettings, IconTrash } from '@tabler/icons-react';
|
import { IconLayoutKanban, IconPencil, IconSettings, IconTrash } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
|
|
||||||
import { useEditModeStore } from '../Views/useEditModeStore';
|
import { useEditModeStore } from '../Views/useEditModeStore';
|
||||||
|
|
||||||
interface GenericTileMenuProps {
|
interface GenericTileMenuProps {
|
||||||
@@ -12,14 +12,12 @@ interface GenericTileMenuProps {
|
|||||||
displayEdit: boolean;
|
displayEdit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GenericTileMenu = (
|
export const GenericTileMenu = ({
|
||||||
{
|
handleClickEdit,
|
||||||
handleClickEdit,
|
handleClickChangePosition,
|
||||||
handleClickChangePosition,
|
handleClickDelete,
|
||||||
handleClickDelete,
|
displayEdit,
|
||||||
displayEdit,
|
}: GenericTileMenuProps) => {
|
||||||
}: GenericTileMenuProps
|
|
||||||
) => {
|
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { useDisclosure } from '@mantine/hooks';
|
|||||||
import { IconChevronDown, IconGripVertical } from '@tabler/icons-react';
|
import { IconChevronDown, IconGripVertical } from '@tabler/icons-react';
|
||||||
import { Reorder, useDragControls } from 'framer-motion';
|
import { Reorder, useDragControls } from 'framer-motion';
|
||||||
import { FC, useEffect, useRef } from 'react';
|
import { FC, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { IDraggableEditableListInputValue } from '~/widgets/widgets';
|
import { IDraggableEditableListInputValue } from '~/widgets/widgets';
|
||||||
|
|
||||||
interface DraggableListProps {
|
interface DraggableListProps {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { useDisclosure } from '@mantine/hooks';
|
|||||||
import { IconChevronDown, IconGripVertical } from '@tabler/icons-react';
|
import { IconChevronDown, IconGripVertical } from '@tabler/icons-react';
|
||||||
import { Reorder, useDragControls } from 'framer-motion';
|
import { Reorder, useDragControls } from 'framer-motion';
|
||||||
import { FC, ReactNode, useEffect, useRef } from 'react';
|
import { FC, ReactNode, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { IDraggableListInputValue } from '~/widgets/widgets';
|
import { IDraggableListInputValue } from '~/widgets/widgets';
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ import { ContextModalProps } from '@mantine/modals';
|
|||||||
import { IconAlertTriangle, IconPlaylistX, IconPlus } from '@tabler/icons-react';
|
import { IconAlertTriangle, IconPlaylistX, IconPlus } from '@tabler/icons-react';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { mapObject } from '~/tools/client/objects';
|
import { mapObject } from '~/tools/client/objects';
|
||||||
import Widgets from '../../../../widgets';
|
|
||||||
import type { IDraggableListInputValue, IWidgetOptionValue } from '~/widgets/widgets';
|
import type { IDraggableListInputValue, IWidgetOptionValue } from '~/widgets/widgets';
|
||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|
||||||
|
import Widgets from '../../../../widgets';
|
||||||
import { InfoCard } from '../../../InfoCard/InfoCard';
|
import { InfoCard } from '../../../InfoCard/InfoCard';
|
||||||
import { DraggableList } from './Inputs/DraggableList';
|
import { DraggableList } from './Inputs/DraggableList';
|
||||||
import { LocationSelection } from './Inputs/LocationSelection';
|
import { LocationSelection } from './Inputs/LocationSelection';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Title } from '@mantine/core';
|
import { Title } from '@mantine/core';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
||||||
import WidgetsDefinitions from '../../../../widgets';
|
|
||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|
||||||
|
import WidgetsDefinitions from '../../../../widgets';
|
||||||
import { useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
import { useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { GenericTileMenu } from '../GenericTileMenu';
|
import { GenericTileMenu } from '../GenericTileMenu';
|
||||||
import { WidgetEditModalInnerProps } from './WidgetsEditModal';
|
import { WidgetEditModalInnerProps } from './WidgetsEditModal';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Button, Group, Stack, Text } from '@mantine/core';
|
import { Button, Group, Stack, Text } from '@mantine/core';
|
||||||
import { ContextModalProps } from '@mantine/modals';
|
import { ContextModalProps } from '@mantine/modals';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Group, Stack } from '@mantine/core';
|
import { Group, Stack } from '@mantine/core';
|
||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useResize } from '~/hooks/use-resize';
|
import { useResize } from '~/hooks/use-resize';
|
||||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||||
import { CategoryType } from '~/types/category';
|
import { CategoryType } from '~/types/category';
|
||||||
import { WrapperType } from '~/types/wrapper';
|
import { WrapperType } from '~/types/wrapper';
|
||||||
|
|
||||||
import { DashboardCategory } from '../Wrappers/Category/Category';
|
import { DashboardCategory } from '../Wrappers/Category/Category';
|
||||||
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
||||||
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
|
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ActionIcon, Button, Text, Tooltip } from '@mantine/core';
|
import { ActionIcon, Button, Text, Tooltip } from '@mantine/core';
|
||||||
import { IconEdit, IconEditOff } from '@tabler/icons-react';
|
import { IconEdit, IconEditOff } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||||
|
|
||||||
import { useEditModeStore } from './useEditModeStore';
|
import { useEditModeStore } from './useEditModeStore';
|
||||||
|
|
||||||
export const ViewToggleButton = () => {
|
export const ViewToggleButton = () => {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import { useLocalStorage } from '@mantine/hooks';
|
|||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { IconDotsVertical, IconShare3 } from '@tabler/icons-react';
|
import { IconDotsVertical, IconShare3 } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { CategoryType } from '~/types/category';
|
import { CategoryType } from '~/types/category';
|
||||||
|
|
||||||
import { useCardStyles } from '../../../layout/Common/useCardStyles';
|
import { useCardStyles } from '../../../layout/Common/useCardStyles';
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { WrapperContent } from '../WrapperContent';
|
import { WrapperContent } from '../WrapperContent';
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
IconTransitionTop,
|
IconTransitionTop,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { CategoryType } from '~/types/category';
|
import { CategoryType } from '~/types/category';
|
||||||
|
|
||||||
import { useCategoryActions } from './useCategoryActions';
|
import { useCategoryActions } from './useCategoryActions';
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
|
|
||||||
interface CategoryEditMenuProps {
|
interface CategoryEditMenuProps {
|
||||||
category: CategoryType;
|
category: CategoryType;
|
||||||
@@ -22,7 +22,7 @@ export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
|
|||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
|
const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
|
||||||
useCategoryActions(configName, category);
|
useCategoryActions(configName, category);
|
||||||
const { t } = useTranslation(['layout/common','common']);
|
const { t } = useTranslation(['layout/common', 'common']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu withinPortal withArrow>
|
<Menu withinPortal withArrow>
|
||||||
@@ -33,28 +33,24 @@ export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
|
|||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
<Menu.Item icon={<IconEdit size={20} />} onClick={edit}>
|
<Menu.Item icon={<IconEdit size={20} />} onClick={edit}>
|
||||||
{t('common:edit')}
|
{t('common:edit')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item icon={<IconTrash size={20} />} onClick={remove}>
|
<Menu.Item icon={<IconTrash size={20} />} onClick={remove}>
|
||||||
{t('common:remove')}
|
{t('common:remove')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Label>
|
<Menu.Label>{t('common:changePosition')}</Menu.Label>
|
||||||
{t('common:changePosition')}
|
|
||||||
</Menu.Label>
|
|
||||||
<Menu.Item icon={<IconTransitionTop size={20} />} onClick={moveCategoryUp}>
|
<Menu.Item icon={<IconTransitionTop size={20} />} onClick={moveCategoryUp}>
|
||||||
{t('menu.moveUp')}
|
{t('menu.moveUp')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item icon={<IconTransitionBottom size={20} />} onClick={moveCategoryDown}>
|
<Menu.Item icon={<IconTransitionBottom size={20} />} onClick={moveCategoryDown}>
|
||||||
{t('menu.moveDown')}
|
{t('menu.moveDown')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Label>
|
<Menu.Label>{t('menu.addCategory', { location: '' })}</Menu.Label>
|
||||||
{t('menu.addCategory',{location: ''})}
|
|
||||||
</Menu.Label>
|
|
||||||
<Menu.Item icon={<IconRowInsertTop size={20} />} onClick={addCategoryAbove}>
|
<Menu.Item icon={<IconRowInsertTop size={20} />} onClick={addCategoryAbove}>
|
||||||
{t('menu.addCategory',{location: t('menu.addAbove')})}
|
{t('menu.addCategory', { location: t('menu.addAbove') })}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item icon={<IconRowInsertBottom size={20} />} onClick={addCategoryBelow}>
|
<Menu.Item icon={<IconRowInsertBottom size={20} />} onClick={addCategoryBelow}>
|
||||||
{t('menu.addCategory',{location: t('menu.addBelow')})}
|
{t('menu.addCategory', { location: t('menu.addBelow') })}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Button, Group, TextInput } from '@mantine/core';
|
|||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { ContextModalProps } from '@mantine/modals';
|
import { ContextModalProps } from '@mantine/modals';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { CategoryType } from '~/types/category';
|
import { CategoryType } from '~/types/category';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
import { CategoryType } from '~/types/category';
|
import { CategoryType } from '~/types/category';
|
||||||
import { WrapperType } from '~/types/wrapper';
|
import { WrapperType } from '~/types/wrapper';
|
||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|
||||||
import { CategoryEditModalInnerProps } from './CategoryEditModal';
|
import { CategoryEditModalInnerProps } from './CategoryEditModal';
|
||||||
|
|
||||||
export const useCategoryActions = (configName: string | undefined, category: CategoryType) => {
|
export const useCategoryActions = (configName: string | undefined, category: CategoryType) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { WrapperType } from '~/types/wrapper';
|
import { WrapperType } from '~/types/wrapper';
|
||||||
|
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { WrapperContent } from '../WrapperContent';
|
import { WrapperContent } from '../WrapperContent';
|
||||||
import { useGridstack } from '../gridstack/use-gridstack';
|
import { useGridstack } from '../gridstack/use-gridstack';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { GridStack } from 'fily-publish-gridstack';
|
import { GridStack } from 'fily-publish-gridstack';
|
||||||
import { MutableRefObject, RefObject } from 'react';
|
import { MutableRefObject, RefObject } from 'react';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
import Widgets from '../../../widgets';
|
|
||||||
import { WidgetWrapper } from '~/widgets/WidgetWrapper';
|
import { WidgetWrapper } from '~/widgets/WidgetWrapper';
|
||||||
import { IWidget, IWidgetDefinition } from '~/widgets/widgets';
|
import { IWidget, IWidgetDefinition } from '~/widgets/widgets';
|
||||||
|
|
||||||
|
import Widgets from '../../../widgets';
|
||||||
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
||||||
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
||||||
import { useGridstackStore } from './gridstack/store';
|
import { useGridstackStore } from './gridstack/store';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { GridItemHTMLElement, GridStack, GridStackNode } from 'fily-publish-gridstack';
|
import { GridItemHTMLElement, GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||||
import { MutableRefObject, RefObject } from 'react';
|
import { MutableRefObject, RefObject } from 'react';
|
||||||
|
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
import { ShapeType } from '~/types/shape';
|
import { ShapeType } from '~/types/shape';
|
||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createWithEqualityFn } from 'zustand/traditional';
|
import { createWithEqualityFn } from 'zustand/traditional';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { GridstackBreakpoints } from '~/constants/gridstack-breakpoints';
|
import { GridstackBreakpoints } from '~/constants/gridstack-breakpoints';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||||
import { MutableRefObject, RefObject, createRef, useEffect, useMemo, useRef } from 'react';
|
import { MutableRefObject, RefObject, createRef, useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { AppType } from '~/types/app';
|
import { AppType } from '~/types/app';
|
||||||
import { AreaType } from '~/types/area';
|
import { AreaType } from '~/types/area';
|
||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { TileWithUnknownLocation, initializeGridstack } from './init-gridstack';
|
import { TileWithUnknownLocation, initializeGridstack } from './init-gridstack';
|
||||||
import { useGridstackStore, useWrapperColumnCount } from './store';
|
import { useGridstackStore, useWrapperColumnCount } from './store';
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import {
|
|||||||
import { IconSearch } from '@tabler/icons-react';
|
import { IconSearch } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||||
|
import { humanFileSize } from '~/tools/humanFileSize';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { humanFileSize } from '~/tools/humanFileSize';
|
|
||||||
import { DebouncedImage } from './DebouncedImage';
|
import { DebouncedImage } from './DebouncedImage';
|
||||||
|
|
||||||
export const IconSelector = forwardRef(
|
export const IconSelector = forwardRef(
|
||||||
@@ -83,9 +83,7 @@ export const IconSelector = forwardRef(
|
|||||||
}
|
}
|
||||||
icon={<DebouncedImage src={value ?? currentValue} width={20} height={20} />}
|
icon={<DebouncedImage src={value ?? currentValue} width={20} height={20} />}
|
||||||
rightSection={
|
rightSection={
|
||||||
(value ?? currentValue).length > 0 ? (
|
(value ?? currentValue).length > 0 ? <CloseButton onClick={() => onChange('')} /> : null
|
||||||
<CloseButton onClick={() => onChange("")} />
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
itemComponent={AutoCompleteItem}
|
itemComponent={AutoCompleteItem}
|
||||||
className={classes.textInput}
|
className={classes.textInput}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ interface InfoCardProps {
|
|||||||
export const InfoCard = ({ bg, cardProp, message, link, hoverProp, position }: InfoCardProps) => {
|
export const InfoCard = ({ bg, cardProp, message, link, hoverProp, position }: InfoCardProps) => {
|
||||||
const { colorScheme } = useMantineTheme();
|
const { colorScheme } = useMantineTheme();
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const content = link? message + ` <a href=\"${link}\" target=\"_blank\">${t('seeMore')}</a>` : message;
|
const content = link
|
||||||
|
? message + ` <a href=\"${link}\" target=\"_blank\">${t('seeMore')}</a>`
|
||||||
|
: message;
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
content,
|
content,
|
||||||
editable: false,
|
editable: false,
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ export const ReviewInputStep = ({ values, prevStep, nextStep }: ReviewInputStepP
|
|||||||
const { t } = useTranslation('manage/users/create');
|
const { t } = useTranslation('manage/users/create');
|
||||||
|
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
const { mutateAsync: createAsync, isLoading, isError, error } = api.user.create.useMutation({
|
const {
|
||||||
|
mutateAsync: createAsync,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = api.user.create.useMutation({
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
void utils.user.all.invalidate();
|
void utils.user.all.invalidate();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,8 +57,10 @@ export const StepCreateAccount = ({
|
|||||||
Create your administrator account
|
Create your administrator account
|
||||||
</Title>
|
</Title>
|
||||||
<Text>
|
<Text>
|
||||||
Your administrator account <b>must be secure</b>, that's why we have so many rules surrounding it.
|
Your administrator account <b>must be secure</b>, that's why we have so many rules
|
||||||
<br/>Try not to make it adminadmin this time...
|
surrounding it.
|
||||||
|
<br />
|
||||||
|
Try not to make it adminadmin this time...
|
||||||
</Text>
|
</Text>
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ services:
|
|||||||
const added = { color: 'green', label: '+' };
|
const added = { color: 'green', label: '+' };
|
||||||
|
|
||||||
export const StepUpdatePathMappings = ({ next }: { next: () => void }) => {
|
export const StepUpdatePathMappings = ({ next }: { next: () => void }) => {
|
||||||
const [selectedTab, setSelectedTab] = useState<TabsValue>("standard_docker");
|
const [selectedTab, setSelectedTab] = useState<TabsValue>('standard_docker');
|
||||||
return (
|
return (
|
||||||
<OnboardingStepWrapper>
|
<OnboardingStepWrapper>
|
||||||
<Title order={2} align="center" mb="md">
|
<Title order={2} align="center" mb="md">
|
||||||
@@ -140,7 +140,9 @@ export const StepUpdatePathMappings = ({ next }: { next: () => void }) => {
|
|||||||
{dockerComposeCommand}
|
{dockerComposeCommand}
|
||||||
</Prism>
|
</Prism>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>Run <Code>docker compose up</Code>.</List.Item>
|
<List.Item>
|
||||||
|
Run <Code>docker compose up</Code>.
|
||||||
|
</List.Item>
|
||||||
<List.Item>Refresh this page and click on "continue"</List.Item>
|
<List.Item>Refresh this page and click on "continue"</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ function getStrength(password: string) {
|
|||||||
score += 1;
|
score += 1;
|
||||||
}
|
}
|
||||||
return (score / goal) * 100;
|
return (score / goal) * 100;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PasswordRequirements = ({ value }: { value: string }) => {
|
export const PasswordRequirements = ({ value }: { value: string }) => {
|
||||||
|
|||||||
@@ -1,23 +1,14 @@
|
|||||||
import {
|
import { ActionIcon, ActionIconProps } from '@mantine/core';
|
||||||
ActionIcon,
|
|
||||||
ActionIconProps,
|
|
||||||
} from '@mantine/core';
|
|
||||||
import { useColorScheme } from '~/hooks/use-colorscheme';
|
|
||||||
import { IconMoonStars, IconSun } from '@tabler/icons-react';
|
import { IconMoonStars, IconSun } from '@tabler/icons-react';
|
||||||
|
import { useColorScheme } from '~/hooks/use-colorscheme';
|
||||||
|
|
||||||
export const ThemeSchemeToggle = (props : Partial<ActionIconProps>) => {
|
export const ThemeSchemeToggle = (props: Partial<ActionIconProps>) => {
|
||||||
const { colorScheme, toggleColorScheme } = useColorScheme();
|
const { colorScheme, toggleColorScheme } = useColorScheme();
|
||||||
const Icon = colorScheme === 'dark' ? IconSun : IconMoonStars;
|
const Icon = colorScheme === 'dark' ? IconSun : IconMoonStars;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionIcon
|
<ActionIcon size={50} variant="outline" radius="md" onClick={toggleColorScheme} {...props}>
|
||||||
size={50}
|
<Icon size="66%" />
|
||||||
variant="outline"
|
|
||||||
radius="md"
|
|
||||||
onClick={toggleColorScheme}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<Icon size="66%"/>
|
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const PolkaElement = ({
|
|||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
transform: `rotate(${rotation}deg)`,
|
transform: `rotate(${rotation}deg)`,
|
||||||
pointerEvents: 'none'
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
className="polka"
|
className="polka"
|
||||||
pos="absolute"
|
pos="absolute"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Group, Image, Text } from '@mantine/core';
|
import { Group, Image, Text } from '@mantine/core';
|
||||||
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
|
||||||
import { usePrimaryGradient } from './useGradient';
|
import { usePrimaryGradient } from './useGradient';
|
||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createStyles } from '@mantine/core';
|
import { createStyles } from '@mantine/core';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
|
||||||
export const useCardStyles = (isCategory: boolean) => {
|
export const useCardStyles = (isCategory: boolean) => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { MantineGradient } from '@mantine/core';
|
import { MantineGradient } from '@mantine/core';
|
||||||
|
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
|
|
||||||
export const usePrimaryGradient = () => {
|
export const usePrimaryGradient = () => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { firstUpperCase } from '~/tools/shared/strings';
|
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
import { firstUpperCase } from '~/tools/shared/strings';
|
||||||
|
|
||||||
export const BoardHeadOverride = () => {
|
export const BoardHeadOverride = () => {
|
||||||
const { config, name } = useConfigContext();
|
const { config, name } = useConfigContext();
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
Group,
|
Group,
|
||||||
HoverCard,
|
HoverCard,
|
||||||
Kbd,
|
|
||||||
Image,
|
Image,
|
||||||
|
Kbd,
|
||||||
Modal,
|
Modal,
|
||||||
Table,
|
Table,
|
||||||
Text,
|
Text,
|
||||||
@@ -31,11 +31,11 @@ import { motion } from 'framer-motion';
|
|||||||
import { InitOptions } from 'i18next';
|
import { InitOptions } from 'i18next';
|
||||||
import { Trans, i18n, useTranslation } from 'next-i18next';
|
import { Trans, i18n, useTranslation } from 'next-i18next';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useConfigStore } from '~/config/store';
|
import { useConfigStore } from '~/config/store';
|
||||||
import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore';
|
import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore';
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
|
|
||||||
import { usePrimaryGradient } from '../../Common/useGradient';
|
import { usePrimaryGradient } from '../../Common/useGradient';
|
||||||
import Credits from './Credits';
|
import Credits from './Credits';
|
||||||
import Tip from './Tip';
|
import Tip from './Tip';
|
||||||
@@ -75,13 +75,7 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod
|
|||||||
opened={opened}
|
opened={opened}
|
||||||
title={
|
title={
|
||||||
<Group spacing="sm">
|
<Group spacing="sm">
|
||||||
<Image
|
<Image alt="Homarr logo" src="/imgs/logo/logo.png" width={30} height={30} fit="contain" />
|
||||||
alt="Homarr logo"
|
|
||||||
src="/imgs/logo/logo.png"
|
|
||||||
width={30}
|
|
||||||
height={30}
|
|
||||||
fit="contain"
|
|
||||||
/>
|
|
||||||
<Title order={3} variant="gradient" gradient={colorGradiant}>
|
<Title order={3} variant="gradient" gradient={colorGradiant}>
|
||||||
{t('about')} Homarr
|
{t('about')} Homarr
|
||||||
</Title>
|
</Title>
|
||||||
@@ -271,13 +265,17 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl
|
|||||||
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
||||||
>
|
>
|
||||||
<Badge color="green" variant="filled">
|
<Badge color="green" variant="filled">
|
||||||
{t('version.new',{ newVersion: newVersionAvailable})}
|
{t('version.new', { newVersion: newVersionAvailable })}
|
||||||
</Badge>
|
</Badge>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</HoverCard.Target>
|
</HoverCard.Target>
|
||||||
<HoverCard.Dropdown>
|
<HoverCard.Dropdown>
|
||||||
<Text>
|
<Text>
|
||||||
{t('version.dropdown', {currentVersion: attributes.packageVersion}).split('{{newVersion}}')[0]}
|
{
|
||||||
|
t('version.dropdown', { currentVersion: attributes.packageVersion }).split(
|
||||||
|
'{{newVersion}}'
|
||||||
|
)[0]
|
||||||
|
}
|
||||||
<b>
|
<b>
|
||||||
<Anchor
|
<Anchor
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -286,7 +284,11 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl
|
|||||||
{newVersionAvailable}
|
{newVersionAvailable}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</b>
|
</b>
|
||||||
{t('version.dropdown', {currentVersion: attributes.packageVersion}).split('{{newVersion}}')[1]}
|
{
|
||||||
|
t('version.dropdown', { currentVersion: attributes.packageVersion }).split(
|
||||||
|
'{{newVersion}}'
|
||||||
|
)[1]
|
||||||
|
}
|
||||||
</Text>
|
</Text>
|
||||||
</HoverCard.Dropdown>
|
</HoverCard.Dropdown>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Anchor, Box, Collapse, Flex, Table, Text } from '@mantine/core';
|
import { Anchor, Box, Collapse, Flex, Table, Text } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore';
|
import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore';
|
||||||
|
|
||||||
export default function Credits() {
|
export default function Credits() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { ConfigType } from '~/types/config';
|
import { ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { useConfigContext } from './provider';
|
import { useConfigContext } from './provider';
|
||||||
import { useConfigStore } from './store';
|
import { useConfigStore } from './store';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ReactNode, createContext, useContext, useEffect, useState } from 'react';
|
import { ReactNode, createContext, useContext, useEffect, useState } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
import { ConfigType } from '~/types/config';
|
import { ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { useConfigStore } from './store';
|
import { useConfigStore } from './store';
|
||||||
|
|
||||||
export type ConfigContextType = {
|
export type ConfigContextType = {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { createWithEqualityFn } from 'zustand/traditional';
|
import { createWithEqualityFn } from 'zustand/traditional';
|
||||||
import { trcpProxyClient } from '~/utils/api';
|
|
||||||
|
|
||||||
import { ConfigType } from '~/types/config';
|
import { ConfigType } from '~/types/config';
|
||||||
|
import { trcpProxyClient } from '~/utils/api';
|
||||||
|
|
||||||
export const useConfigStore = createWithEqualityFn<UseConfigStoreType>(
|
export const useConfigStore = createWithEqualityFn<UseConfigStoreType>(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const shouldRedirectToOnboard = async (): Promise<boolean> => {
|
|||||||
const cacheAndGetUserCount = async () => {
|
const cacheAndGetUserCount = async () => {
|
||||||
cachedUserCount = await client.user.count.query();
|
cachedUserCount = await client.user.count.query();
|
||||||
return cachedUserCount === 0;
|
return cachedUserCount === 0;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (!env.DATABASE_URL?.startsWith('file:')) {
|
if (!env.DATABASE_URL?.startsWith('file:')) {
|
||||||
return await cacheAndGetUserCount();
|
return await cacheAndGetUserCount();
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { Badge, Button, Group, Image, Stack, Text, Title } from '@mantine/core';
|
|||||||
import { IconDownload, IconExternalLink, IconPlayerPlay } from '@tabler/icons-react';
|
import { IconDownload, IconExternalLink, IconPlayerPlay } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
|
|
||||||
import { RequestModal } from '../overseerr/RequestModal';
|
import { RequestModal } from '../overseerr/RequestModal';
|
||||||
import { Result } from '../overseerr/SearchResult';
|
import { Result } from '../overseerr/SearchResult';
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import Consola from 'consola';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
import { useColorTheme } from '~/tools/color';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { useColorTheme } from '~/tools/color';
|
|
||||||
import { MovieResult } from './Movie.d';
|
import { MovieResult } from './Movie.d';
|
||||||
import { Result } from './SearchResult';
|
import { Result } from './SearchResult';
|
||||||
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import Head from 'next/head';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import pageNotFoundImage from '~/images/undraw_page_not_found_re_e9o6.svg';
|
import pageNotFoundImage from '~/images/undraw_page_not_found_re_e9o6.svg';
|
||||||
import { pageNotFoundNamespaces } from '~/tools/server/translation-namespaces';
|
|
||||||
|
|
||||||
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
|
import { pageNotFoundNamespaces } from '~/tools/server/translation-namespaces';
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
Group,
|
Group,
|
||||||
|
Image,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
Title,
|
Title,
|
||||||
Image,
|
|
||||||
UnstyledButton,
|
UnstyledButton,
|
||||||
createStyles,
|
createStyles,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
|||||||
@@ -10,54 +10,60 @@ import { AppType } from '~/types/app';
|
|||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
export const appRouter = createTRPCRouter({
|
export const appRouter = createTRPCRouter({
|
||||||
ping: publicProcedure.input(z.object({
|
ping: publicProcedure
|
||||||
id: z.string(),
|
.input(
|
||||||
configName: z.string()
|
z.object({
|
||||||
})).query(async ({ input }) => {
|
id: z.string(),
|
||||||
const agent = new https.Agent({ rejectUnauthorized: false });
|
configName: z.string(),
|
||||||
const config = getConfig(input.configName);
|
})
|
||||||
const app = config.apps.find((app) => app.id === input.id);
|
)
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
const agent = new https.Agent({ rejectUnauthorized: false });
|
||||||
|
const config = getConfig(input.configName);
|
||||||
|
const app = config.apps.find((app) => app.id === input.id);
|
||||||
|
|
||||||
if (!app?.url) {
|
if (!app?.url) {
|
||||||
Consola.error(`App ${input} not found`);
|
Consola.error(`App ${input} not found`);
|
||||||
throw new TRPCError({
|
|
||||||
code: 'NOT_FOUND',
|
|
||||||
cause: input,
|
|
||||||
message: `App ${input.id} was not found`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const res = await axios
|
|
||||||
.get(app.url, { httpsAgent: agent, timeout: 10000 })
|
|
||||||
.then((response) => ({
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
state: isStatusOk(app as AppType, response.status) ? 'online' : 'offline'
|
|
||||||
}))
|
|
||||||
.catch((error: AxiosError) => {
|
|
||||||
if (error.response) {
|
|
||||||
return {
|
|
||||||
state: isStatusOk(app as AppType, error.response.status) ? 'online' : 'offline',
|
|
||||||
status: error.response.status,
|
|
||||||
statusText: error.response.statusText,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.code === 'ECONNABORTED') {
|
|
||||||
Consola.error(`Ping timed out for app with id '${input.id}' in config '${input.configName}' -> url: ${app.url})`);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: 'TIMEOUT',
|
|
||||||
cause: input,
|
|
||||||
message: `Ping timed out`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Consola.error(`Unexpected response: ${error.message}`);
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'UNPROCESSABLE_CONTENT',
|
code: 'NOT_FOUND',
|
||||||
cause: input,
|
cause: input,
|
||||||
message: `Unexpected response: ${error.message}`,
|
message: `App ${input.id} was not found`,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
return res;
|
const res = await axios
|
||||||
}),
|
.get(app.url, { httpsAgent: agent, timeout: 10000 })
|
||||||
|
.then((response) => ({
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
state: isStatusOk(app as AppType, response.status) ? 'online' : 'offline',
|
||||||
|
}))
|
||||||
|
.catch((error: AxiosError) => {
|
||||||
|
if (error.response) {
|
||||||
|
return {
|
||||||
|
state: isStatusOk(app as AppType, error.response.status) ? 'online' : 'offline',
|
||||||
|
status: error.response.status,
|
||||||
|
statusText: error.response.statusText,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code === 'ECONNABORTED') {
|
||||||
|
Consola.error(
|
||||||
|
`Ping timed out for app with id '${input.id}' in config '${input.configName}' -> url: ${app.url})`
|
||||||
|
);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'TIMEOUT',
|
||||||
|
cause: input,
|
||||||
|
message: `Ping timed out`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Consola.error(`Unexpected response: ${error.message}`);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'UNPROCESSABLE_CONTENT',
|
||||||
|
cause: input,
|
||||||
|
message: `Unexpected response: ${error.message}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
|
import { removeTrailingSlash } from 'next/dist/shared/lib/router/utils/remove-trailing-slash';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { checkIntegrationsType } from '~/tools/client/app-properties';
|
import { checkIntegrationsType } from '~/tools/client/app-properties';
|
||||||
import { getConfig } from '~/tools/config/getConfig';
|
import { getConfig } from '~/tools/config/getConfig';
|
||||||
import { MediaRequestListWidget } from '~/widgets/media-requests/MediaRequestListTile';
|
import { MediaRequestListWidget } from '~/widgets/media-requests/MediaRequestListTile';
|
||||||
|
import { MediaRequestStatsWidget } from '~/widgets/media-requests/MediaRequestStatsTile';
|
||||||
import { MediaRequest, Users } from '~/widgets/media-requests/media-request-types';
|
import { MediaRequest, Users } from '~/widgets/media-requests/media-request-types';
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||||
import { MediaRequestStatsWidget } from '~/widgets/media-requests/MediaRequestStatsTile';
|
|
||||||
import { removeTrailingSlash } from 'next/dist/shared/lib/router/utils/remove-trailing-slash';
|
|
||||||
|
|
||||||
export const mediaRequestsRouter = createTRPCRouter({
|
export const mediaRequestsRouter = createTRPCRouter({
|
||||||
allMedia: publicProcedure
|
allMedia: publicProcedure
|
||||||
@@ -33,9 +33,11 @@ export const mediaRequestsRouter = createTRPCRouter({
|
|||||||
})
|
})
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
const body = (await response.json()) as OverseerrResponse;
|
const body = (await response.json()) as OverseerrResponse;
|
||||||
let appUrl = input.widget.properties.replaceLinksWithExternalHost && app.behaviour.externalUrl?.length > 0
|
let appUrl =
|
||||||
? app.behaviour.externalUrl
|
input.widget.properties.replaceLinksWithExternalHost &&
|
||||||
: app.url;
|
app.behaviour.externalUrl?.length > 0
|
||||||
|
? app.behaviour.externalUrl
|
||||||
|
: app.url;
|
||||||
|
|
||||||
appUrl = removeTrailingSlash(appUrl);
|
appUrl = removeTrailingSlash(appUrl);
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ const retrieveDetailsForItem = async (
|
|||||||
backdropPath: series.backdropPath,
|
backdropPath: series.backdropPath,
|
||||||
posterPath: series.backdropPath,
|
posterPath: series.backdropPath,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
const movieResponse = await fetch(`${baseUrl}/api/v1/movie/${id}`, {
|
const movieResponse = await fetch(`${baseUrl}/api/v1/movie/${id}`, {
|
||||||
headers,
|
headers,
|
||||||
|
|||||||
@@ -2,21 +2,18 @@ import { Jellyfin } from '@jellyfin/sdk';
|
|||||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models';
|
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models';
|
||||||
import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api';
|
import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api';
|
||||||
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
|
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
|
||||||
|
|
||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
|
||||||
|
|
||||||
import { ConfigAppType } from '~/types/app';
|
|
||||||
import { checkIntegrationsType, findAppProperty } from '~/tools/client/app-properties';
|
import { checkIntegrationsType, findAppProperty } from '~/tools/client/app-properties';
|
||||||
import { getConfig } from '~/tools/config/getConfig';
|
import { getConfig } from '~/tools/config/getConfig';
|
||||||
|
import { PlexClient } from '~/tools/server/sdk/plex/plexClient';
|
||||||
import { trimStringEnding } from '~/tools/shared/strings';
|
import { trimStringEnding } from '~/tools/shared/strings';
|
||||||
import { GenericMediaServer } from '~/types/api/media-server/media-server';
|
import { GenericMediaServer } from '~/types/api/media-server/media-server';
|
||||||
import { MediaServersResponseType } from '~/types/api/media-server/response';
|
import { MediaServersResponseType } from '~/types/api/media-server/response';
|
||||||
import { GenericCurrentlyPlaying, GenericSessionInfo } from '~/types/api/media-server/session-info';
|
import { GenericCurrentlyPlaying, GenericSessionInfo } from '~/types/api/media-server/session-info';
|
||||||
import { PlexClient } from '~/tools/server/sdk/plex/plexClient';
|
import { ConfigAppType } from '~/types/app';
|
||||||
|
|
||||||
|
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
const jellyfin = new Jellyfin({
|
const jellyfin = new Jellyfin({
|
||||||
clientInfo: {
|
clientInfo: {
|
||||||
@@ -108,7 +105,13 @@ const handleServer = async (app: ConfigAppType): Promise<GenericMediaServer | un
|
|||||||
return {
|
return {
|
||||||
type: 'jellyfin',
|
type: 'jellyfin',
|
||||||
appId: app.id,
|
appId: app.id,
|
||||||
serverAddress: trimStringEnding(app.url, ['/web/index.html#!/home.html', '/web', '/web/index.html', '/web/', '/web/index.html#']),
|
serverAddress: trimStringEnding(app.url, [
|
||||||
|
'/web/index.html#!/home.html',
|
||||||
|
'/web',
|
||||||
|
'/web/index.html',
|
||||||
|
'/web/',
|
||||||
|
'/web/index.html#',
|
||||||
|
]),
|
||||||
version: infoApi.data.Version ?? undefined,
|
version: infoApi.data.Version ?? undefined,
|
||||||
sessions: sessions
|
sessions: sessions
|
||||||
.filter((session) => session.NowPlayingItem)
|
.filter((session) => session.NowPlayingItem)
|
||||||
@@ -180,7 +183,13 @@ const handleServer = async (app: ConfigAppType): Promise<GenericMediaServer | un
|
|||||||
|
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return {
|
return {
|
||||||
serverAddress: trimStringEnding(app.url, ['/web', '/web/index.html', '/web/index.html#!', '/web/index.html#!/settings/web/general', '/web/']),
|
serverAddress: trimStringEnding(app.url, [
|
||||||
|
'/web',
|
||||||
|
'/web/index.html',
|
||||||
|
'/web/index.html#!',
|
||||||
|
'/web/index.html#!/settings/web/general',
|
||||||
|
'/web/',
|
||||||
|
]),
|
||||||
sessions: [],
|
sessions: [],
|
||||||
type: 'plex',
|
type: 'plex',
|
||||||
appId: app.id,
|
appId: app.id,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const rssRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (input.feedUrls.length === 0) {
|
if (input.feedUrls.length === 0) {
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
const GeoTz = require('browser-geo-tz/dist/geotz.js');
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
|
const GeoTz = require('browser-geo-tz/dist/geotz.js');
|
||||||
|
|
||||||
export const timezoneRouter = createTRPCRouter({
|
export const timezoneRouter = createTRPCRouter({
|
||||||
at: publicProcedure
|
at: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
longitude: z.number(),
|
longitude: z.number(),
|
||||||
latitude: z.number(),
|
latitude: z.number(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const timezone = GeoTz.find(input.latitude,input.longitude);
|
const timezone = GeoTz.find(input.latitude, input.longitude);
|
||||||
return Array.isArray(timezone) ? timezone[0] : timezone;
|
return Array.isArray(timezone) ? timezone[0] : timezone;
|
||||||
}),
|
}),
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { BackendConfigType, ConfigType } from '~/types/config';
|
import { BackendConfigType, ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { configExists } from './configExists';
|
import { configExists } from './configExists';
|
||||||
import { getFallbackConfig } from './getFallbackConfig';
|
import { getFallbackConfig } from './getFallbackConfig';
|
||||||
import { readConfig } from './readConfig';
|
import { readConfig } from './readConfig';
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ConfigType } from '~/types/config';
|
import { ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import defaultConfig from '../../../data/configs/default.json';
|
import defaultConfig from '../../../data/configs/default.json';
|
||||||
|
|
||||||
export const getFallbackConfig = (name?: string) => ({
|
export const getFallbackConfig = (name?: string) => ({
|
||||||
@@ -11,7 +12,7 @@ export const getFallbackConfig = (name?: string) => ({
|
|||||||
export const getStaticFallbackConfig = (name: string): ConfigType => ({
|
export const getStaticFallbackConfig = (name: string): ConfigType => ({
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
configProperties: {
|
configProperties: {
|
||||||
name: name
|
name: name,
|
||||||
},
|
},
|
||||||
categories: [
|
categories: [
|
||||||
{
|
{
|
||||||
@@ -55,7 +56,7 @@ export const getStaticFallbackConfig = (name: string): ConfigType => ({
|
|||||||
},
|
},
|
||||||
accessibility: {
|
accessibility: {
|
||||||
disablePingPulse: false,
|
disablePingPulse: false,
|
||||||
replacePingDotsWithIcons: false
|
replacePingDotsWithIcons: false,
|
||||||
},
|
},
|
||||||
pageTitle: 'Homarr ⭐️',
|
pageTitle: 'Homarr ⭐️',
|
||||||
logoImageUrl: '/imgs/logo/logo.png',
|
logoImageUrl: '/imgs/logo/logo.png',
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import Consola from 'consola';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { fetchCity } from '~/server/api/routers/weather';
|
import { fetchCity } from '~/server/api/routers/weather';
|
||||||
import { IntegrationField } from '~/types/app';
|
import { IntegrationField } from '~/types/app';
|
||||||
|
|
||||||
import { BackendConfigType, ConfigType } from '~/types/config';
|
import { BackendConfigType, ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { getConfig } from './getConfig';
|
import { getConfig } from './getConfig';
|
||||||
|
|
||||||
export const getFrontendConfig = async (name: string): Promise<ConfigType> => {
|
export const getFrontendConfig = async (name: string): Promise<ConfigType> => {
|
||||||
@@ -139,11 +139,11 @@ const migrateAppConfigs = (config: BackendConfigType) => {
|
|||||||
...app,
|
...app,
|
||||||
appearance: {
|
appearance: {
|
||||||
...app.appearance,
|
...app.appearance,
|
||||||
appNameStatus: app.appearance.appNameStatus?? 'normal',
|
appNameStatus: app.appearance.appNameStatus ?? 'normal',
|
||||||
positionAppName: app.appearance.positionAppName?? 'column',
|
positionAppName: app.appearance.positionAppName ?? 'column',
|
||||||
appNameFontSize: app.appearance.appNameFontSize?? 16,
|
appNameFontSize: app.appearance.appNameFontSize ?? 16,
|
||||||
lineClampAppName: app.appearance.lineClampAppName?? 1,
|
lineClampAppName: app.appearance.lineClampAppName ?? 1,
|
||||||
}
|
},
|
||||||
}))
|
})),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { BackendConfigType } from '~/types/config';
|
import { BackendConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { generateConfigPath } from './generateConfigPath';
|
import { generateConfigPath } from './generateConfigPath';
|
||||||
|
|
||||||
export function writeConfig(config: BackendConfigType) {
|
export function writeConfig(config: BackendConfigType) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ParsedUrlQuery } from 'querystring';
|
|||||||
export const checkForSessionOrAskForLogin = (
|
export const checkForSessionOrAskForLogin = (
|
||||||
context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>,
|
context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>,
|
||||||
session: Session | null,
|
session: Session | null,
|
||||||
accessCallback: () => boolean,
|
accessCallback: () => boolean
|
||||||
): GetServerSidePropsResult<any> | undefined => {
|
): GetServerSidePropsResult<any> | undefined => {
|
||||||
if (!session?.user) {
|
if (!session?.user) {
|
||||||
console.log('detected logged out user!');
|
console.log('detected logged out user!');
|
||||||
@@ -26,8 +26,8 @@ export const checkForSessionOrAskForLogin = (
|
|||||||
if (!accessCallback()) {
|
if (!accessCallback()) {
|
||||||
return {
|
return {
|
||||||
props: {},
|
props: {},
|
||||||
notFound: true
|
notFound: true,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import axios from 'axios';
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { trimStringEnding } from '~/tools/shared/strings';
|
import { trimStringEnding } from '~/tools/shared/strings';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
adGuardApiFilteringStatusSchema,
|
adGuardApiFilteringStatusSchema,
|
||||||
adGuardApiStatsResponseSchema,
|
adGuardApiStatsResponseSchema,
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ describe('PiHole API client', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice'){
|
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice') {
|
||||||
countTriedRequests += 1;
|
countTriedRequests += 1;
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
@@ -213,9 +213,9 @@ describe('PiHole API client', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice'){
|
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice') {
|
||||||
countTriedRequests += 1;
|
countTriedRequests += 1;
|
||||||
if(countTriedRequests < 10) {
|
if (countTriedRequests < 10) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
status: 'disabled',
|
status: 'disabled',
|
||||||
});
|
});
|
||||||
@@ -261,7 +261,7 @@ describe('PiHole API client', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice'){
|
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice') {
|
||||||
countTriedRequests += 1;
|
countTriedRequests += 1;
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
status: 'disabled',
|
status: 'disabled',
|
||||||
@@ -303,9 +303,9 @@ describe('PiHole API client', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice'){
|
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice') {
|
||||||
countTriedRequests += 1;
|
countTriedRequests += 1;
|
||||||
if(countTriedRequests < 10) {
|
if (countTriedRequests < 10) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
});
|
});
|
||||||
@@ -351,7 +351,7 @@ describe('PiHole API client', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice'){
|
if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=nice') {
|
||||||
countTriedRequests += 1;
|
countTriedRequests += 1;
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
status: 'disabled',
|
status: 'disabled',
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { trimStringEnding } from '~/tools/shared/strings';
|
import { trimStringEnding } from '~/tools/shared/strings';
|
||||||
|
|
||||||
import { PiHoleApiStatusChangeResponse, PiHoleApiSummaryResponse } from './piHole.type';
|
import { PiHoleApiStatusChangeResponse, PiHoleApiSummaryResponse } from './piHole.type';
|
||||||
|
|
||||||
export class PiHoleClient {
|
export class PiHoleClient {
|
||||||
@@ -62,18 +63,18 @@ export class PiHoleClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let loops = 0; loops < 10; loops++){
|
for (let loops = 0; loops < 10; loops++) {
|
||||||
const summary = await this.getSummary()
|
const summary = await this.getSummary();
|
||||||
if (summary.status === action + 'd'){
|
if (summary.status === action + 'd') {
|
||||||
return { status: summary.status } as PiHoleApiStatusChangeResponse;
|
return { status: summary.status } as PiHoleApiStatusChangeResponse;
|
||||||
}
|
}
|
||||||
await new Promise ((resolve) => { setTimeout(resolve, 50)});
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 50);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(
|
new Error(`Although PiHole received the command, it failed to update it's status: ${json}`)
|
||||||
`Although PiHole received the command, it failed to update it's status: ${json}`
|
);
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { Element, xml2js } from 'xml-js';
|
import { Element, xml2js } from 'xml-js';
|
||||||
|
import { GenericCurrentlyPlaying, GenericSessionInfo } from '~/types/api/media-server/session-info';
|
||||||
import {
|
|
||||||
GenericCurrentlyPlaying,
|
|
||||||
GenericSessionInfo,
|
|
||||||
} from '~/types/api/media-server/session-info';
|
|
||||||
|
|
||||||
export class PlexClient {
|
export class PlexClient {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -44,7 +40,6 @@ export class PlexClient {
|
|||||||
const { videoCodec, videoFrameRate, audioCodec, audioChannels, height, width, bitrate } =
|
const { videoCodec, videoFrameRate, audioCodec, audioChannels, height, width, bitrate } =
|
||||||
mediaElement;
|
mediaElement;
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: sessionElement?.id as string | undefined,
|
id: sessionElement?.id as string | undefined,
|
||||||
username: userElement?.title ?? ('Anonymous' as string),
|
username: userElement?.title ?? ('Anonymous' as string),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const generateDefaultApp = (wrapperId: string): AppType =>
|
|||||||
appNameStatus: 'normal',
|
appNameStatus: 'normal',
|
||||||
positionAppName: 'column',
|
positionAppName: 'column',
|
||||||
lineClampAppName: 1,
|
lineClampAppName: 1,
|
||||||
appNameFontSize: 16
|
appNameFontSize: 16,
|
||||||
},
|
},
|
||||||
network: {
|
network: {
|
||||||
enabledStatusChecker: true,
|
enabledStatusChecker: true,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
||||||
|
|
||||||
import { UsenetQueueItem } from '~/widgets/useNet/types';
|
import { UsenetQueueItem } from '~/widgets/useNet/types';
|
||||||
|
|
||||||
export type NormalizedDownloadQueueResponse = {
|
export type NormalizedDownloadQueueResponse = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Icon, IconKey, IconPassword, IconUser } from '@tabler/icons-react';
|
import { Icon, IconKey, IconPassword, IconUser } from '@tabler/icons-react';
|
||||||
import { Property } from 'csstype'
|
import { Property } from 'csstype';
|
||||||
|
|
||||||
import { TileBaseType } from './tile';
|
import { TileBaseType } from './tile';
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ interface AppNetworkType {
|
|||||||
|
|
||||||
interface AppAppearanceType {
|
interface AppAppearanceType {
|
||||||
iconUrl: string;
|
iconUrl: string;
|
||||||
appNameStatus: "normal"|"hover"|"hidden";
|
appNameStatus: 'normal' | 'hover' | 'hidden';
|
||||||
positionAppName: Property.FlexDirection;
|
positionAppName: Property.FlexDirection;
|
||||||
appNameFontSize: number;
|
appNameFontSize: number;
|
||||||
lineClampAppName: number;
|
lineClampAppName: number;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IWidget } from '~/widgets/widgets';
|
import { IWidget } from '~/widgets/widgets';
|
||||||
|
|
||||||
import { AppType, ConfigAppType } from './app';
|
import { AppType, ConfigAppType } from './app';
|
||||||
import { CategoryType } from './category';
|
import { CategoryType } from './category';
|
||||||
import { SettingsType } from './settings';
|
import { SettingsType } from './settings';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
export const fromDate = (seconds: number, date = Date.now()) => {
|
export const fromDate = (seconds: number, date = Date.now()) => {
|
||||||
return new Date(date + seconds * 1000);
|
return new Date(date + seconds * 1000);
|
||||||
@@ -8,4 +8,3 @@ export const fromDate = (seconds: number, date = Date.now()) => {
|
|||||||
export const generateSessionToken = () => {
|
export const generateSessionToken = () => {
|
||||||
return randomUUID();
|
return randomUUID();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ComponentType } from 'react';
|
import { ComponentType } from 'react';
|
||||||
|
|
||||||
import Widgets from '.';
|
|
||||||
import { HomarrCardWrapper } from '~/components/Dashboard/Tiles/HomarrCardWrapper';
|
import { HomarrCardWrapper } from '~/components/Dashboard/Tiles/HomarrCardWrapper';
|
||||||
import { WidgetsMenu } from '~/components/Dashboard/Tiles/Widgets/WidgetsMenu';
|
import { WidgetsMenu } from '~/components/Dashboard/Tiles/Widgets/WidgetsMenu';
|
||||||
|
|
||||||
|
import Widgets from '.';
|
||||||
import ErrorBoundary from './boundary';
|
import ErrorBoundary from './boundary';
|
||||||
import { IWidget } from './widgets';
|
import { IWidget } from './widgets';
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
Group,
|
Group,
|
||||||
Image,
|
Image,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Divider,
|
|
||||||
Stack,
|
Stack,
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
@@ -27,12 +27,12 @@ import {
|
|||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import React from 'react';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
||||||
import { IconSelector } from '~/components/IconSelector/IconSelector';
|
import { IconSelector } from '~/components/IconSelector/IconSelector';
|
||||||
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IDraggableEditableListInputValue, IWidget } from '../widgets';
|
import { IDraggableEditableListInputValue, IWidget } from '../widgets';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ const definition = defineWidget({
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
info: true,
|
info: true,
|
||||||
infoLink: "https://homarr.dev/docs/widgets/bookmarks/",
|
infoLink: 'https://homarr.dev/docs/widgets/bookmarks/',
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
type: 'draggable-editable-list',
|
type: 'draggable-editable-list',
|
||||||
@@ -84,11 +84,11 @@ const definition = defineWidget({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return t('item.validation.length', {shortest: "1", longest: "100"});
|
return t('item.validation.length', { shortest: '1', longest: '100' });
|
||||||
},
|
},
|
||||||
href: (value) => {
|
href: (value) => {
|
||||||
if (!z.string().min(1).max(200).safeParse(value).success) {
|
if (!z.string().min(1).max(200).safeParse(value).success) {
|
||||||
return t('item.validation.length', {shortest: "1", longest: "200"});
|
return t('item.validation.length', { shortest: '1', longest: '200' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!z.string().url().safeParse(value).success) {
|
if (!z.string().url().safeParse(value).success) {
|
||||||
@@ -102,7 +102,7 @@ const definition = defineWidget({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return t('item.validation.length', {shortest: "1", longest: "400"});
|
return t('item.validation.length', { shortest: '1', longest: '400' });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validateInputOnChange: true,
|
validateInputOnChange: true,
|
||||||
@@ -174,11 +174,7 @@ const definition = defineWidget({
|
|||||||
} satisfies IDraggableEditableListInputValue<BookmarkItem>,
|
} satisfies IDraggableEditableListInputValue<BookmarkItem>,
|
||||||
layout: {
|
layout: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
data: [
|
data: [{ value: 'autoGrid' }, { value: 'horizontal' }, { value: 'vertical' }],
|
||||||
{ value: 'autoGrid', },
|
|
||||||
{ value: 'horizontal', },
|
|
||||||
{ value: 'vertical', },
|
|
||||||
],
|
|
||||||
defaultValue: 'autoGrid',
|
defaultValue: 'autoGrid',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -223,10 +219,12 @@ function BookmarkWidgetTile({ widget }: BookmarkWidgetTileProps) {
|
|||||||
case 'autoGrid':
|
case 'autoGrid':
|
||||||
return (
|
return (
|
||||||
<Stack h="100%" spacing={0}>
|
<Stack h="100%" spacing={0}>
|
||||||
<Title size="h4" px="0.25rem">{widget.properties.name}</Title>
|
<Title size="h4" px="0.25rem">
|
||||||
|
{widget.properties.name}
|
||||||
|
</Title>
|
||||||
<Box
|
<Box
|
||||||
className={classes.grid}
|
className={classes.grid}
|
||||||
mr={isEditModeEnabled && widget.properties.name === "" ? 'xl' : undefined}
|
mr={isEditModeEnabled && widget.properties.name === '' ? 'xl' : undefined}
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
{widget.properties.items.map((item: BookmarkItem, index) => (
|
{widget.properties.items.map((item: BookmarkItem, index) => (
|
||||||
@@ -239,10 +237,12 @@ function BookmarkWidgetTile({ widget }: BookmarkWidgetTileProps) {
|
|||||||
href={item.href}
|
href={item.href}
|
||||||
target={item.openNewTab ? '_blank' : undefined}
|
target={item.openNewTab ? '_blank' : undefined}
|
||||||
withBorder
|
withBorder
|
||||||
bg={colorScheme === 'dark' ? colors.dark[5].concat('80') : colors.blue[0].concat('80')}
|
bg={
|
||||||
|
colorScheme === 'dark' ? colors.dark[5].concat('80') : colors.blue[0].concat('80')
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
'&:hover': { backgroundColor: fn.primaryColor().concat('40'), }, //'40' = 25% opacity
|
'&:hover': { backgroundColor: fn.primaryColor().concat('40') }, //'40' = 25% opacity
|
||||||
flex:'1 1 auto',
|
flex: '1 1 auto',
|
||||||
}}
|
}}
|
||||||
display="flex"
|
display="flex"
|
||||||
>
|
>
|
||||||
@@ -265,29 +265,29 @@ function BookmarkWidgetTile({ widget }: BookmarkWidgetTileProps) {
|
|||||||
type="auto"
|
type="auto"
|
||||||
h="100%"
|
h="100%"
|
||||||
offsetScrollbars
|
offsetScrollbars
|
||||||
mr={isEditModeEnabled && widget.properties.name === ""? 'xl' : undefined}
|
mr={isEditModeEnabled && widget.properties.name === '' ? 'xl' : undefined}
|
||||||
styles={{
|
styles={{
|
||||||
viewport:{
|
viewport: {
|
||||||
//mantine being mantine again... this might break. Needed for taking 100% of widget space
|
//mantine being mantine again... this might break. Needed for taking 100% of widget space
|
||||||
'& div[style="min-width: 100%; display: table;"]':{
|
'& div[style="min-width: 100%; display: table;"]': {
|
||||||
display: 'flex !important',
|
display: 'flex !important',
|
||||||
height:'100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex direction={flexDirection} gap="0" h="100%" w="100%">
|
||||||
direction={flexDirection}
|
|
||||||
gap="0"
|
|
||||||
h="100%"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
{widget.properties.items.map((item: BookmarkItem, index) => (
|
{widget.properties.items.map((item: BookmarkItem, index) => (
|
||||||
<div key={index} style={{ display: 'flex', flex: '1', flexDirection: flexDirection, }}>
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{ display: 'flex', flex: '1', flexDirection: flexDirection }}
|
||||||
|
>
|
||||||
<Divider
|
<Divider
|
||||||
m="3px"
|
m="3px"
|
||||||
orientation={ widget.properties.layout !== 'vertical' ? 'vertical' : 'horizontal' }
|
orientation={
|
||||||
color={index === 0 ? "transparent" : undefined}
|
widget.properties.layout !== 'vertical' ? 'vertical' : 'horizontal'
|
||||||
|
}
|
||||||
|
color={index === 0 ? 'transparent' : undefined}
|
||||||
/>
|
/>
|
||||||
<Card
|
<Card
|
||||||
px="md"
|
px="md"
|
||||||
@@ -298,13 +298,13 @@ function BookmarkWidgetTile({ widget }: BookmarkWidgetTileProps) {
|
|||||||
radius="md"
|
radius="md"
|
||||||
bg="transparent"
|
bg="transparent"
|
||||||
sx={{
|
sx={{
|
||||||
'&:hover': { backgroundColor: fn.primaryColor().concat('40'),}, //'40' = 25% opacity
|
'&:hover': { backgroundColor: fn.primaryColor().concat('40') }, //'40' = 25% opacity
|
||||||
flex:'1 1 auto',
|
flex: '1 1 auto',
|
||||||
overflow: 'unset',
|
overflow: 'unset',
|
||||||
}}
|
}}
|
||||||
display="flex"
|
display="flex"
|
||||||
>
|
>
|
||||||
<BookmarkItemContent item={item}/>
|
<BookmarkItemContent item={item} />
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -320,26 +320,28 @@ function BookmarkWidgetTile({ widget }: BookmarkWidgetTileProps) {
|
|||||||
const BookmarkItemContent = ({ item }: { item: BookmarkItem }) => {
|
const BookmarkItemContent = ({ item }: { item: BookmarkItem }) => {
|
||||||
const { colorScheme } = useMantineTheme();
|
const { colorScheme } = useMantineTheme();
|
||||||
return (
|
return (
|
||||||
<Group spacing="0rem 1rem">
|
<Group spacing="0rem 1rem">
|
||||||
<Image
|
<Image
|
||||||
hidden={item.hideIcon}
|
hidden={item.hideIcon}
|
||||||
src={item.iconUrl}
|
src={item.iconUrl}
|
||||||
width={47}
|
width={47}
|
||||||
height={47}
|
height={47}
|
||||||
fit="contain"
|
fit="contain"
|
||||||
withPlaceholder />
|
withPlaceholder
|
||||||
<Stack spacing={0}>
|
/>
|
||||||
<Text size="md">{item.name}</Text>
|
<Stack spacing={0}>
|
||||||
<Text
|
<Text size="md">{item.name}</Text>
|
||||||
color={colorScheme === 'dark' ? "gray.6" : "gray.7"}
|
<Text
|
||||||
size="sm"
|
color={colorScheme === 'dark' ? 'gray.6' : 'gray.7'}
|
||||||
hidden={item.hideHostname}
|
size="sm"
|
||||||
>
|
hidden={item.hideHostname}
|
||||||
{new URL(item.href).hostname}
|
>
|
||||||
</Text>
|
{new URL(item.href).hostname}
|
||||||
</Stack>
|
</Text>
|
||||||
</Group>
|
</Stack>
|
||||||
)};
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const useStyles = createStyles(() => ({
|
const useStyles = createStyles(() => ({
|
||||||
grid: {
|
grid: {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { IconBrandGithub, IconBug, IconInfoCircle, IconRefresh } from '@tabler/i
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
import { withTranslation } from 'next-i18next';
|
import { withTranslation } from 'next-i18next';
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
import { WidgetsMenu } from '~/components/Dashboard/Tiles/Widgets/WidgetsMenu';
|
import { WidgetsMenu } from '~/components/Dashboard/Tiles/Widgets/WidgetsMenu';
|
||||||
|
|
||||||
import { IWidget } from './widgets';
|
import { IWidget } from './widgets';
|
||||||
|
|
||||||
type ErrorBoundaryState = {
|
type ErrorBoundaryState = {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Divider, ScrollArea, createStyles } from '@mantine/core';
|
import { Divider, ScrollArea, createStyles } from '@mantine/core';
|
||||||
import { useViewportSize } from '@mantine/hooks';
|
import { useViewportSize } from '@mantine/hooks';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LidarrMediaDisplay,
|
LidarrMediaDisplay,
|
||||||
RadarrMediaDisplay,
|
RadarrMediaDisplay,
|
||||||
ReadarrMediaDisplay,
|
ReadarrMediaDisplay,
|
||||||
SonarrMediaDisplay,
|
SonarrMediaDisplay,
|
||||||
} from '~/modules/common';
|
} from '~/modules/common';
|
||||||
|
|
||||||
import { MediasType } from './type';
|
import { MediasType } from './type';
|
||||||
|
|
||||||
interface MediaListProps {
|
interface MediaListProps {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ColorScheme, useMantineTheme } from '@mantine/core';
|
import { ColorScheme, useMantineTheme } from '@mantine/core';
|
||||||
|
|
||||||
import { isToday } from '~/tools/shared/time/date.tool';
|
import { isToday } from '~/tools/shared/time/date.tool';
|
||||||
|
|
||||||
export const getBgColorByDateAndTheme = (colorScheme: ColorScheme, date: Date) => {
|
export const getBgColorByDateAndTheme = (colorScheme: ColorScheme, date: Date) => {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Group, Stack, Text } from '@mantine/core';
|
import { Group, Stack, Text } from '@mantine/core';
|
||||||
import { IconArrowNarrowDown, IconArrowNarrowUp } from '@tabler/icons-react';
|
import { IconArrowNarrowDown, IconArrowNarrowUp } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { RouterOutputs } from '~/utils/api';
|
|
||||||
|
|
||||||
import { bytes } from '~/tools/bytesHelper';
|
import { bytes } from '~/tools/bytesHelper';
|
||||||
|
import { RouterOutputs } from '~/utils/api';
|
||||||
|
|
||||||
interface DashDotCompactNetworkProps {
|
interface DashDotCompactNetworkProps {
|
||||||
info: DashDotInfo;
|
info: DashDotInfo;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Group, Stack, Text } from '@mantine/core';
|
import { Group, Stack, Text } from '@mantine/core';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { api } from '~/utils/api';
|
|
||||||
|
|
||||||
import { bytes } from '~/tools/bytesHelper';
|
import { bytes } from '~/tools/bytesHelper';
|
||||||
import { percentage } from '~/tools/shared/math/percentage.tool';
|
import { percentage } from '~/tools/shared/math/percentage.tool';
|
||||||
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { DashDotInfo } from './DashDotCompactNetwork';
|
import { DashDotInfo } from './DashDotCompactNetwork';
|
||||||
|
|
||||||
interface DashDotCompactStorageProps {
|
interface DashDotCompactStorageProps {
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { RouterInputs, api } from '~/utils/api';
|
import { RouterInputs, api } from '~/utils/api';
|
||||||
import { UsenetHistoryRequestParams, UsenetInfoRequestParams, UsenetPauseRequestParams, UsenetQueueRequestParams, UsenetResumeRequestParams } from '../useNet/types';
|
|
||||||
|
import {
|
||||||
|
UsenetHistoryRequestParams,
|
||||||
|
UsenetInfoRequestParams,
|
||||||
|
UsenetPauseRequestParams,
|
||||||
|
UsenetQueueRequestParams,
|
||||||
|
UsenetResumeRequestParams,
|
||||||
|
} from '../useNet/types';
|
||||||
|
|
||||||
const POLLING_INTERVAL = 2000;
|
const POLLING_INTERVAL = 2000;
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import timezones from 'dayjs/plugin/timezone';
|
|||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useSetSafeInterval } from '~/hooks/useSetSafeInterval';
|
||||||
import { getLanguageByCode } from '~/tools/language';
|
import { getLanguageByCode } from '~/tools/language';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { useSetSafeInterval } from '~/hooks/useSetSafeInterval';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
|
|
||||||
@@ -108,8 +108,8 @@ function DateTile({ widget }: DateTileProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = createStyles(()=>({
|
const useStyles = createStyles(() => ({
|
||||||
wrapper:{
|
wrapper: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'space-evenly',
|
justifyContent: 'space-evenly',
|
||||||
@@ -117,17 +117,17 @@ const useStyles = createStyles(()=>({
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
gap: 0,
|
gap: 0,
|
||||||
},
|
},
|
||||||
clock:{
|
clock: {
|
||||||
lineHeight: '1',
|
lineHeight: '1',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
fontSize: '2.125rem',
|
fontSize: '2.125rem',
|
||||||
},
|
},
|
||||||
extras:{
|
extras: {
|
||||||
lineHeight: '1',
|
lineHeight: '1',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}
|
},
|
||||||
}))
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State which updates when the minute is changing
|
* State which updates when the minute is changing
|
||||||
@@ -142,7 +142,7 @@ const useDateState = (location?: { latitude: number; longitude: number }) => {
|
|||||||
const { data: userWithSettings } = api.user.withSettings.useQuery(undefined, {
|
const { data: userWithSettings } = api.user.withSettings.useQuery(undefined, {
|
||||||
enabled: !!sessionData?.user,
|
enabled: !!sessionData?.user,
|
||||||
});
|
});
|
||||||
const userLanguage = userWithSettings?.settings.language;
|
const userLanguage = userWithSettings?.settings.language;
|
||||||
const [date, setDate] = useState(getNewDate(timezone));
|
const [date, setDate] = useState(getNewDate(timezone));
|
||||||
const setSafeInterval = useSetSafeInterval();
|
const setSafeInterval = useSetSafeInterval();
|
||||||
const timeoutRef = useRef<NodeJS.Timeout>(); // reference for initial timeout until first minute change
|
const timeoutRef = useRef<NodeJS.Timeout>(); // reference for initial timeout until first minute change
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import { useElementSize } from '@mantine/hooks';
|
|||||||
import { IconDeviceGamepad, IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
|
import { IconDeviceGamepad, IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { WidgetLoading } from '../loading';
|
import { WidgetLoading } from '../loading';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
@@ -84,15 +84,19 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.status.length === 0) {
|
if (data.status.length === 0) {
|
||||||
return(
|
return (
|
||||||
<Center h="100%">
|
<Center h="100%">
|
||||||
<Stack align="center">
|
<Stack align="center">
|
||||||
<IconDeviceGamepad size={40} strokeWidth={1}/>
|
<IconDeviceGamepad size={40} strokeWidth={1} />
|
||||||
<Title align="center" order={6}>{t('modules/dns-hole-controls:descriptor.errors.general.title')}</Title>
|
<Title align="center" order={6}>
|
||||||
<Text align="center">{t('modules/dns-hole-controls:descriptor.errors.general.text')}</Text>
|
{t('modules/dns-hole-controls:descriptor.errors.general.title')}
|
||||||
|
</Title>
|
||||||
|
<Text align="center">
|
||||||
|
{t('modules/dns-hole-controls:descriptor.errors.general.text')}
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@@ -136,15 +140,18 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await mutateAsync({
|
await mutateAsync(
|
||||||
action: 'enable',
|
{
|
||||||
configName,
|
action: 'enable',
|
||||||
appsToChange: getDnsStatus()?.disabled,
|
configName,
|
||||||
},{
|
appsToChange: getDnsStatus()?.disabled,
|
||||||
onSettled: () => {
|
},
|
||||||
reFetchSummaryDns();
|
{
|
||||||
|
onSettled: () => {
|
||||||
|
reFetchSummaryDns();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}}
|
}}
|
||||||
disabled={getDnsStatus()?.disabled.length === 0 || fetchingDnsSummary || changingStatus}
|
disabled={getDnsStatus()?.disabled.length === 0 || fetchingDnsSummary || changingStatus}
|
||||||
leftIcon={<IconPlayerPlay size={20} />}
|
leftIcon={<IconPlayerPlay size={20} />}
|
||||||
@@ -156,15 +163,18 @@ function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await mutateAsync({
|
await mutateAsync(
|
||||||
action: 'disable',
|
{
|
||||||
configName,
|
action: 'disable',
|
||||||
appsToChange: getDnsStatus()?.enabled,
|
configName,
|
||||||
},{
|
appsToChange: getDnsStatus()?.enabled,
|
||||||
onSettled: () => {
|
},
|
||||||
reFetchSummaryDns();
|
{
|
||||||
|
onSettled: () => {
|
||||||
|
reFetchSummaryDns();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}}
|
}}
|
||||||
disabled={getDnsStatus()?.enabled.length === 0 || fetchingDnsSummary || changingStatus}
|
disabled={getDnsStatus()?.enabled.length === 0 || fetchingDnsSummary || changingStatus}
|
||||||
leftIcon={<IconPlayerStop size={20} />}
|
leftIcon={<IconPlayerStop size={20} />}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
import { formatNumber, formatPercentage } from '~/tools/client/math';
|
||||||
import { RouterOutputs, api } from '~/utils/api';
|
import { RouterOutputs, api } from '~/utils/api';
|
||||||
|
|
||||||
import { formatNumber, formatPercentage } from '~/tools/client/math';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { WidgetLoading } from '../loading';
|
import { WidgetLoading } from '../loading';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ import { Datum, ResponsiveLine, Serie } from '@nivo/line';
|
|||||||
import { IconDownload, IconUpload } from '@tabler/icons-react';
|
import { IconDownload, IconUpload } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { AppAvatar } from '~/components/AppAvatar';
|
import { AppAvatar } from '~/components/AppAvatar';
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useGetDownloadClientsQueue } from './useGetNetworkSpeed';
|
|
||||||
import { useColorTheme } from '~/tools/color';
|
import { useColorTheme } from '~/tools/color';
|
||||||
import { humanFileSize } from '~/tools/humanFileSize';
|
import { humanFileSize } from '~/tools/humanFileSize';
|
||||||
import {
|
import {
|
||||||
NormalizedDownloadQueueResponse,
|
NormalizedDownloadQueueResponse,
|
||||||
TorrentTotalDownload,
|
TorrentTotalDownload,
|
||||||
} from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
} from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
||||||
|
|
||||||
import definition, { ITorrentNetworkTraffic } from './TorrentNetworkTrafficTile';
|
import definition, { ITorrentNetworkTraffic } from './TorrentNetworkTrafficTile';
|
||||||
|
import { useGetDownloadClientsQueue } from './useGetNetworkSpeed';
|
||||||
|
|
||||||
interface TorrentNetworkTrafficTileProps {
|
interface TorrentNetworkTrafficTileProps {
|
||||||
widget: ITorrentNetworkTraffic;
|
widget: ITorrentNetworkTraffic;
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import iframe from './iframe/IFrameTile';
|
|||||||
import mediaRequestsList from './media-requests/MediaRequestListTile';
|
import mediaRequestsList from './media-requests/MediaRequestListTile';
|
||||||
import mediaRequestsStats from './media-requests/MediaRequestStatsTile';
|
import mediaRequestsStats from './media-requests/MediaRequestStatsTile';
|
||||||
import mediaServer from './media-server/MediaServerTile';
|
import mediaServer from './media-server/MediaServerTile';
|
||||||
|
import notebook from './notebook/NotebookWidgetTile';
|
||||||
import rss from './rss/RssWidgetTile';
|
import rss from './rss/RssWidgetTile';
|
||||||
import torrent from './torrent/TorrentTile';
|
import torrent from './torrent/TorrentTile';
|
||||||
import usenet from './useNet/UseNetTile';
|
import usenet from './useNet/UseNetTile';
|
||||||
import videoStream from './video/VideoStreamTile';
|
import videoStream from './video/VideoStreamTile';
|
||||||
import weather from './weather/WeatherTile';
|
import weather from './weather/WeatherTile';
|
||||||
import notebook from './notebook/NotebookWidgetTile';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
calendar,
|
calendar,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function MediaRequestStatsTile({ widget }: MediaRequestStatsWidgetProps) {
|
|||||||
const {
|
const {
|
||||||
data: usersData,
|
data: usersData,
|
||||||
isFetching: usersFetching,
|
isFetching: usersFetching,
|
||||||
isLoading: usersLoading
|
isLoading: usersLoading,
|
||||||
} = useUsersQuery(widget);
|
} = useUsersQuery(widget);
|
||||||
const { ref, height } = useElementSize();
|
const { ref, height } = useElementSize();
|
||||||
const { colorScheme } = useMantineTheme();
|
const { colorScheme } = useMantineTheme();
|
||||||
@@ -128,7 +128,7 @@ function MediaRequestStatsTile({ widget }: MediaRequestStatsWidgetProps) {
|
|||||||
p={0}
|
p={0}
|
||||||
component="a"
|
component="a"
|
||||||
href={user.userLink}
|
href={user.userLink}
|
||||||
target={widget.properties.openInNewTab ? "_blank" : "_self"}
|
target={widget.properties.openInNewTab ? '_blank' : '_self'}
|
||||||
mah={95}
|
mah={95}
|
||||||
mih={55}
|
mih={55}
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { MediaRequestListWidget } from './MediaRequestListTile';
|
|
||||||
import { MediaRequestStatsWidget } from './MediaRequestStatsTile';
|
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
export const useMediaRequestQuery = (widget: MediaRequestListWidget|MediaRequestStatsWidget) => {
|
import { MediaRequestListWidget } from './MediaRequestListTile';
|
||||||
|
import { MediaRequestStatsWidget } from './MediaRequestStatsTile';
|
||||||
|
|
||||||
|
export const useMediaRequestQuery = (widget: MediaRequestListWidget | MediaRequestStatsWidget) => {
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
return api.mediaRequest.allMedia.useQuery(
|
return api.mediaRequest.allMedia.useQuery(
|
||||||
{ configName: configName!, widget: widget },
|
{ configName: configName!, widget: widget },
|
||||||
@@ -13,7 +14,7 @@ export const useMediaRequestQuery = (widget: MediaRequestListWidget|MediaRequest
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUsersQuery = (widget: MediaRequestListWidget|MediaRequestStatsWidget) => {
|
export const useUsersQuery = (widget: MediaRequestListWidget | MediaRequestStatsWidget) => {
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
return api.mediaRequest.users.useQuery(
|
return api.mediaRequest.users.useQuery(
|
||||||
{ configName: configName!, widget: widget },
|
{ configName: configName!, widget: widget },
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import {
|
|||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconAlertTriangle, IconMovie } from '@tabler/icons-react';
|
import { IconAlertTriangle, IconMovie } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { AppAvatar } from '~/components/AppAvatar';
|
import { AppAvatar } from '~/components/AppAvatar';
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { useGetMediaServers } from './useGetMediaServers';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
import { TableRow } from './TableRow';
|
import { TableRow } from './TableRow';
|
||||||
|
import { useGetMediaServers } from './useGetMediaServers';
|
||||||
|
|
||||||
const definition = defineWidget({
|
const definition = defineWidget({
|
||||||
id: 'media-server',
|
id: 'media-server',
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ import {
|
|||||||
IconQuestionMark,
|
IconQuestionMark,
|
||||||
IconVideo,
|
IconVideo,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
import { GenericSessionInfo } from '~/types/api/media-server/session-info';
|
import { GenericSessionInfo } from '~/types/api/media-server/session-info';
|
||||||
|
|
||||||
export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo }) => {
|
export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo }) => {
|
||||||
|
|
||||||
if (!session.currentlyPlaying) {
|
if (!session.currentlyPlaying) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import duration from 'dayjs/plugin/duration';
|
|||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useCardStyles } from '~/components/layout/Common/useCardStyles';
|
import { useCardStyles } from '~/components/layout/Common/useCardStyles';
|
||||||
|
|
||||||
import { MIN_WIDTH_MOBILE } from '~/constants/constants';
|
import { MIN_WIDTH_MOBILE } from '~/constants/constants';
|
||||||
import { NormalizedDownloadQueueResponse } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
import { NormalizedDownloadQueueResponse } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
||||||
import { AppIntegrationType } from '~/types/app';
|
import { AppIntegrationType } from '~/types/app';
|
||||||
|
|
||||||
import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed';
|
import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed';
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import duration from 'dayjs/plugin/duration';
|
|||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useConfigContext } from '~/config/provider';
|
import { useConfigContext } from '~/config/provider';
|
||||||
import { MIN_WIDTH_MOBILE } from '~/constants/constants';
|
import { MIN_WIDTH_MOBILE } from '~/constants/constants';
|
||||||
import { humanFileSize } from '~/tools/humanFileSize';
|
import { humanFileSize } from '~/tools/humanFileSize';
|
||||||
import { AppIntegrationType } from '~/types/app';
|
import { AppIntegrationType } from '~/types/app';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useGetUsenetInfo,
|
useGetUsenetInfo,
|
||||||
usePauseUsenetQueueMutation,
|
usePauseUsenetQueueMutation,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ interface WeatherIconProps {
|
|||||||
* @param code weather code from api
|
* @param code weather code from api
|
||||||
* @returns weather tile component
|
* @returns weather tile component
|
||||||
*/
|
*/
|
||||||
export const WeatherIcon = ({ code, size=50 }: WeatherIconProps) => {
|
export const WeatherIcon = ({ code, size = 50 }: WeatherIconProps) => {
|
||||||
const { t } = useTranslation('modules/weather');
|
const { t } = useTranslation('modules/weather');
|
||||||
|
|
||||||
const { icon: Icon, name } =
|
const { icon: Icon, name } =
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
IconCloudRain,
|
IconCloudRain,
|
||||||
IconMapPin,
|
IconMapPin,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
import { WeatherIcon } from './WeatherIcon';
|
import { WeatherIcon } from './WeatherIcon';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const definition = defineWidget({
|
const definition = defineWidget({
|
||||||
id: 'weather',
|
id: 'weather',
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Icon } from '@tabler/icons-react';
|
import { Icon } from '@tabler/icons-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { AreaType } from '~/types/area';
|
import { AreaType } from '~/types/area';
|
||||||
import { ShapeType } from '~/types/shape';
|
import { ShapeType } from '~/types/shape';
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import { SSRConfig } from 'next-i18next';
|
import { SSRConfig } from 'next-i18next';
|
||||||
|
|
||||||
import { ParsedUrlQuery } from 'querystring';
|
import { ParsedUrlQuery } from 'querystring';
|
||||||
|
|
||||||
import { describe, expect, it, vitest } from 'vitest';
|
import { describe, expect, it, vitest } from 'vitest';
|
||||||
|
import * as serverAuthModule from '~/server/auth';
|
||||||
|
import { ConfigType } from '~/types/config';
|
||||||
|
|
||||||
import { getServerSideProps } from '../../../src/pages/board/[slug]';
|
import { getServerSideProps } from '../../../src/pages/board/[slug]';
|
||||||
|
|
||||||
import * as configExistsModule from '../../../src/tools/config/configExists';
|
import * as configExistsModule from '../../../src/tools/config/configExists';
|
||||||
import * as getFrontendConfigModule from '../../../src/tools/config/getFrontendConfig';
|
import * as getFrontendConfigModule from '../../../src/tools/config/getFrontendConfig';
|
||||||
import * as getServerSideTranslationsModule from '../../../src/tools/server/getServerSideTranslations';
|
import * as getServerSideTranslationsModule from '../../../src/tools/server/getServerSideTranslations';
|
||||||
|
|
||||||
import * as serverAuthModule from '~/server/auth';
|
|
||||||
import { ConfigType } from '~/types/config';
|
|
||||||
|
|
||||||
vitest.mock('./../../server/auth.ts', () => ({
|
vitest.mock('./../../server/auth.ts', () => ({
|
||||||
getServerAuthSession: () => null,
|
getServerAuthSession: () => null,
|
||||||
}));
|
}));
|
||||||
@@ -159,8 +154,8 @@ describe('[slug] page', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(response).toEqual({
|
expect(response).toEqual({
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/auth/login?redirectAfterLogin=/board/my-authentication-board",
|
destination: '/auth/login?redirectAfterLogin=/board/my-authentication-board',
|
||||||
permanent: false
|
permanent: false,
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user