mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
Translations
This commit is contained in:
@@ -164,28 +164,30 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"widgets": [{
|
||||
"id": "date",
|
||||
"properties": {
|
||||
"display24HourFormat": true
|
||||
},
|
||||
"area": {
|
||||
"type": "wrapper",
|
||||
"widgets": [
|
||||
{
|
||||
"id": "date",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 3
|
||||
"display24HourFormat": true
|
||||
},
|
||||
"size": {
|
||||
"width": 4,
|
||||
"height": 2
|
||||
"area": {
|
||||
"type": "wrapper",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
"size": {
|
||||
"width": 4,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
],
|
||||
"settings": {
|
||||
"common": {
|
||||
"searchEngine": {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"actions": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"delete": "Delete",
|
||||
"ok": "Okay",
|
||||
"edit": "Edit",
|
||||
"changePosition": "Change position",
|
||||
@@ -11,6 +13,11 @@
|
||||
"settings": "Settings",
|
||||
"dangerZone": "Danger zone"
|
||||
},
|
||||
"secrets": {
|
||||
"apiKey": "Api key",
|
||||
"username": "Username",
|
||||
"password": "Password"
|
||||
},
|
||||
"tip": "Tip: ",
|
||||
"time": {
|
||||
"seconds": "seconds",
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
{
|
||||
"actionIcon": {
|
||||
"tooltip": "Add a service"
|
||||
},
|
||||
"modal": {
|
||||
"title": "Add service",
|
||||
"form": {
|
||||
"validation": {
|
||||
"invalidUrl": "Please enter a valid URL",
|
||||
"noStatusCodeSelected": "Please select a status code"
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"options": {
|
||||
"title": "Options",
|
||||
"form": {
|
||||
"serviceName": {
|
||||
"label": "Service name",
|
||||
"placeholder": "Plex"
|
||||
},
|
||||
"iconUrl": {
|
||||
"label": "Icon URL"
|
||||
},
|
||||
"serviceUrl": {
|
||||
"label": "Service URL"
|
||||
},
|
||||
"onClickUrl": {
|
||||
"label": "On Click URL"
|
||||
},
|
||||
"serviceType": {
|
||||
"label": "Service type",
|
||||
"defaultValue": "Other",
|
||||
"placeholder": "Pick one"
|
||||
},
|
||||
"category": {
|
||||
"label": "Category",
|
||||
"placeholder": "Select a category or create a new one",
|
||||
"nothingFound": "Nothing found",
|
||||
"createLabel": "+ Create {{query}}"
|
||||
},
|
||||
"integrations": {
|
||||
"apiKey": {
|
||||
"label": "API key",
|
||||
"placeholder": "Your API key",
|
||||
"validation": {
|
||||
"noKey": "Invalid Key"
|
||||
},
|
||||
"tip": {
|
||||
"text": "Get your API key",
|
||||
"link": "here."
|
||||
}
|
||||
},
|
||||
"qBittorrent": {
|
||||
"username": {
|
||||
"label": "Username",
|
||||
"placeholder": "admin",
|
||||
"validation": {
|
||||
"invalidUsername": "Invalid username"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "adminadmin",
|
||||
"validation": {
|
||||
"invalidPassword": "Invalid password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deluge": {
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "password",
|
||||
"validation": {
|
||||
"invalidPassword": "Invalid password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transmission": {
|
||||
"username": {
|
||||
"label": "Username",
|
||||
"placeholder": "admin",
|
||||
"validation": {
|
||||
"invalidUsername": "Invalid username"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "adminadmin",
|
||||
"validation": {
|
||||
"invalidPassword": "Invalid password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nzbget": {
|
||||
"username": {
|
||||
"label": "Username",
|
||||
"placeholder": "admin",
|
||||
"validation": {
|
||||
"invalidUsername": "Invalid username"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "password",
|
||||
"validation": {
|
||||
"invalidPassword": "Invalid password"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"advancedOptions": {
|
||||
"title": "Advanced options",
|
||||
"form": {
|
||||
"openServiceInNewTab": {
|
||||
"label": "Open service in new tab"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": {
|
||||
"content": "Add service"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"modal": {
|
||||
"title": "Modify a service",
|
||||
"buttons": {
|
||||
"save": "Save service"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"labels": {
|
||||
"settings": "Settings",
|
||||
"dangerZone": "Danger zone"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"delete": "Delete"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"accordions": {
|
||||
"downloads": {
|
||||
"text": "Your downloads",
|
||||
"torrents": "Your Torrent downloads",
|
||||
"usenet": "Your Usenet downloads"
|
||||
},
|
||||
"others": {
|
||||
"text": "Others"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"modal": {
|
||||
"title": "Add a new tile to your Dashboard",
|
||||
"text": "You can use tiles, to make Homarr's dashboard yours. You may choose your desired tiles from the collection below and drag them anywhere on the dashboard where you like."
|
||||
"title": "Add a new tile",
|
||||
"text": "Tiles are the main element of homarr. They allow you to configure the dashboard and display the information you want."
|
||||
},
|
||||
"actionIcon": {
|
||||
"tooltip": "Add a tile"
|
||||
|
||||
59
public/locales/en/layout/modals/add-app.json
Normal file
59
public/locales/en/layout/modals/add-app.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"tabs": {
|
||||
"general": "General",
|
||||
"behaviour": "Behaviour",
|
||||
"network": "Network",
|
||||
"appearance": "Appearance",
|
||||
"integration": "Integration"
|
||||
},
|
||||
"general": {
|
||||
"appname": {
|
||||
"label": "App name",
|
||||
"description": "Used for displaying the app on the dashboard"
|
||||
},
|
||||
"internalAddress": {
|
||||
"label": "Internal address",
|
||||
"description": "Internal IP of the app"
|
||||
},
|
||||
"externalAddress": {
|
||||
"label": "External address",
|
||||
"description": "Url that will be opened in the browser when clicking on the app"
|
||||
}
|
||||
},
|
||||
"behaviour": {
|
||||
"isOpeningNewTab": {
|
||||
"label": "Open in new tab",
|
||||
"description": "Open the link in a new tab"
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"statusChecker": {
|
||||
"label": "Status checker",
|
||||
"description": "Sends a simple HTTP / HTTPS request to check if your app is online"
|
||||
},
|
||||
"statusCodes": {
|
||||
"label": "HTTP status codes",
|
||||
"description": "Determines what response codes are allowed for this app to be 'Online'"
|
||||
}
|
||||
},
|
||||
"appearance": {
|
||||
"icon": {
|
||||
"label": "App Icon",
|
||||
"description": "Logo of your app displayed in your dashboard. (Must return a body content containg an image)"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"type": {
|
||||
"label": "Integration configuration",
|
||||
"description": "Treats this app as the selected integration and provides you with per-app configuration",
|
||||
"placeholder": "Select an integration"
|
||||
},
|
||||
"secrets": {
|
||||
"description": "To update a secret, enter a value and click the save button. To remove a secret, use the clear button.",
|
||||
"warning": "Please note that Homarr removes secrets from the configuration for security reasons. Thus, you can only either define or unset any credentials. Your credentials act as the main access for your integrations and you should <strong>never</strong> share them with anybody else. Make sure to <strong>store and manage your secrets safely</strong>.",
|
||||
"clear": "Clear secret",
|
||||
"save": "Save secret",
|
||||
"update": "Update secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,19 @@
|
||||
"credits": {
|
||||
"madeWithLove": "Made with ❤️ by @"
|
||||
},
|
||||
"grow": "Grow grid (take all space)"
|
||||
"grow": "Grow grid (take all space)",
|
||||
"yayout": {
|
||||
"title": "Dashboard yayout",
|
||||
"main": "Main",
|
||||
"sidebar": "Sidebar",
|
||||
"cannotturnoff": "Cannot be turned off",
|
||||
"dashboardlayout": "Dashboard layout",
|
||||
"enablersidebar": "Enable right sidebar",
|
||||
"enablelsidebar": "Enable left sidebar",
|
||||
"enablesearchbar": "Enable search bar",
|
||||
"enabledocker": "Enable docker integration",
|
||||
"enableping": "Enable pings",
|
||||
"enablelsidebardescription": "Optional. Can be used for apps and integrations only",
|
||||
"enablersidebardescription": "Optional. Can be used for apps and integrations only"
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,6 @@ const AppShelf = (props: any) => {
|
||||
const [activeId, setActiveId] = useState(null);
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
const { t } = useTranslation('layout/app-shelf');
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Button, Flex, Grid, NumberInput, Select, SelectItem } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
|
||||
interface ChangePositionModalProps {
|
||||
@@ -44,6 +45,8 @@ export const ChangePositionModal = ({
|
||||
onSubmit(form.values.x, form.values.y, form.values.width, form.values.height);
|
||||
};
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Grid>
|
||||
@@ -94,9 +97,9 @@ export const ChangePositionModal = ({
|
||||
|
||||
<Flex justify="end" gap="sm" mt="md">
|
||||
<Button onClick={() => onCancel()} variant="light" color="gray">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Alert, Button, createStyles, Group, Stack, Tabs, Text, ThemeIcon } from '@mantine/core';
|
||||
import { Alert, Button, Group, Stack, Tabs, Text, ThemeIcon } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { ContextModalProps } from '@mantine/modals';
|
||||
import {
|
||||
@@ -30,7 +30,7 @@ export const EditAppModal = ({
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<{ app: AppType; allowAppNamePropagation: boolean }>) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation(['layout/modals/add-app', 'common']);
|
||||
const { name: configName, config } = useConfigContext();
|
||||
const updateConfig = useConfigStore((store) => store.updateConfig);
|
||||
const [allowAppNamePropagation, setAllowAppNamePropagation] = useState<boolean>(
|
||||
@@ -137,68 +137,76 @@ export const EditAppModal = ({
|
||||
</Stack>
|
||||
|
||||
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onTabChange={(tab) => setActiveTab(tab as EditAppModalTab)}
|
||||
defaultValue="general"
|
||||
<Stack
|
||||
justify="space-between"
|
||||
style={{
|
||||
minHeight: 300,
|
||||
}}
|
||||
>
|
||||
<Tabs.List grow>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['name', 'url']} />}
|
||||
icon={<IconAdjustments size={14} />}
|
||||
value="general"
|
||||
>
|
||||
General
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['behaviour.onClickUrl']} />}
|
||||
icon={<IconClick size={14} />}
|
||||
value="behaviour"
|
||||
>
|
||||
Behaviour
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={[]} />}
|
||||
icon={<IconAccessPoint size={14} />}
|
||||
value="network"
|
||||
>
|
||||
Network
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['appearance.iconUrl']} />}
|
||||
icon={<IconBrush size={14} />}
|
||||
value="appearance"
|
||||
>
|
||||
Appearance
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={[]} />}
|
||||
icon={<IconPlug size={14} />}
|
||||
value="integration"
|
||||
>
|
||||
Integration
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onTabChange={(tab) => setActiveTab(tab as EditAppModalTab)}
|
||||
defaultValue="general"
|
||||
radius="md"
|
||||
>
|
||||
<Tabs.List grow>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['name', 'url']} />}
|
||||
icon={<IconAdjustments size={14} />}
|
||||
value="general"
|
||||
>
|
||||
{t('tabs.general')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['behaviour.onClickUrl']} />}
|
||||
icon={<IconClick size={14} />}
|
||||
value="behaviour"
|
||||
>
|
||||
{t('tabs.behaviour')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={[]} />}
|
||||
icon={<IconAccessPoint size={14} />}
|
||||
value="network"
|
||||
>
|
||||
{t('tabs.network')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['appearance.iconUrl']} />}
|
||||
icon={<IconBrush size={14} />}
|
||||
value="appearance"
|
||||
>
|
||||
{t('tabs.appearance')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={[]} />}
|
||||
icon={<IconPlug size={14} />}
|
||||
value="integration"
|
||||
>
|
||||
{t('tabs.integration')}
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<GeneralTab form={form} openTab={(targetTab) => setActiveTab(targetTab)} />
|
||||
<BehaviourTab form={form} />
|
||||
<NetworkTab form={form} />
|
||||
<AppearanceTab
|
||||
form={form}
|
||||
disallowAppNameProgagation={() => setAllowAppNamePropagation(false)}
|
||||
allowAppNamePropagation={allowAppNamePropagation}
|
||||
/>
|
||||
<IntegrationTab form={form} />
|
||||
</Tabs>
|
||||
<GeneralTab form={form} openTab={(targetTab) => setActiveTab(targetTab)} />
|
||||
<BehaviourTab form={form} />
|
||||
<NetworkTab form={form} />
|
||||
<AppearanceTab
|
||||
form={form}
|
||||
disallowAppNameProgagation={() => setAllowAppNamePropagation(false)}
|
||||
allowAppNamePropagation={allowAppNamePropagation}
|
||||
/>
|
||||
<IntegrationTab form={form} />
|
||||
</Tabs>
|
||||
|
||||
<Group position="right" mt={100}>
|
||||
<Button onClick={closeModal} px={50} variant="light" color="gray">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button disabled={!form.isValid()} px={50} type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Group>
|
||||
<Group position="right" mt="md">
|
||||
<Button onClick={closeModal} px={50} variant="light" color="gray">
|
||||
{t('common:actions.cancel')}
|
||||
</Button>
|
||||
<Button disabled={!form.isValid()} px={50} type="submit">
|
||||
{t('common:actions.save')}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ export const AppearanceTab = ({
|
||||
disallowAppNameProgagation,
|
||||
allowAppNamePropagation,
|
||||
}: AppearanceTabProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
@@ -26,8 +26,8 @@ export const AppearanceTab = ({
|
||||
defaultValue={form.values.appearance.iconUrl}
|
||||
className={classes.textInput}
|
||||
icon={<DebouncedAppIcon form={form} width={20} height={20} />}
|
||||
label="App Icon"
|
||||
description="Logo of your app displayed in your dashboard. Must return a body content containg an image"
|
||||
label={t('appearance.icon.label')}
|
||||
description={t('appearance.icon.description')}
|
||||
variant="default"
|
||||
withAsterisk
|
||||
required
|
||||
|
||||
@@ -9,21 +9,13 @@ interface BehaviourTabProps {
|
||||
}
|
||||
|
||||
export const BehaviourTab = ({ form }: BehaviourTabProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
|
||||
return (
|
||||
<Tabs.Panel value="behaviour" pt="xs">
|
||||
<TextInput
|
||||
icon={<IconClick size={16} />}
|
||||
label="On click url"
|
||||
description="Overrides the app URL when clicking on the app"
|
||||
placeholder="URL that should be opened instead when clicking on the app"
|
||||
variant="default"
|
||||
mb="md"
|
||||
{...form.getInputProps('behaviour.onClickUrl')}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label="Open in new tab"
|
||||
label={t('behaviour.isOpeningNewTab.label')}
|
||||
description={t('behaviour.isOpeningNewTab.description')}
|
||||
{...form.getInputProps('behaviour.isOpeningNewTab', { type: 'checkbox' })}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Tabs, Text, TextInput } from '@mantine/core';
|
||||
import { Tabs, TextInput } from '@mantine/core';
|
||||
import { UseFormReturnType } from '@mantine/form';
|
||||
import { IconCursorText, IconLink } from '@tabler/icons';
|
||||
import { IconClick, IconCursorText, IconLink } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { AppType } from '../../../../../../types/app';
|
||||
import { EditAppModalTab } from '../type';
|
||||
@@ -11,43 +11,42 @@ interface GeneralTabProps {
|
||||
}
|
||||
|
||||
export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
return (
|
||||
<Tabs.Panel value="general" pt="lg">
|
||||
<Tabs.Panel value="general" pt="sm">
|
||||
<TextInput
|
||||
icon={<IconCursorText size={16} />}
|
||||
label="App name"
|
||||
description="Used for displaying the app on the dashboard"
|
||||
label={t('general.appname.label')}
|
||||
description={t('general.appname.description')}
|
||||
placeholder="My example app"
|
||||
variant="default"
|
||||
mb="md"
|
||||
withAsterisk
|
||||
required
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
icon={<IconLink size={16} />}
|
||||
label="App url"
|
||||
description={
|
||||
<Text>
|
||||
URL that will be opened when clicking on the app. Can be overwritten using
|
||||
<Text
|
||||
onClick={() => openTab('behaviour')}
|
||||
variant="link"
|
||||
span
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
on click URL{' '}
|
||||
</Text>
|
||||
when using external URLs to enhance security.
|
||||
</Text>
|
||||
}
|
||||
label={t('general.internalAddress.label')}
|
||||
description={t('general.internalAddress.description')}
|
||||
placeholder="https://google.com"
|
||||
variant="default"
|
||||
withAsterisk
|
||||
required
|
||||
{...form.getInputProps('url')}
|
||||
onChange={(e) => {
|
||||
form.setFieldValue('behaviour.onClickUrl', e.target.value);
|
||||
form.setFieldValue('url', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
icon={<IconClick size={16} />}
|
||||
label={t('general.externalAddress.label')}
|
||||
description={t('general.externalAddress.description')}
|
||||
placeholder="https://homarr.mywebsite.com/"
|
||||
variant="default"
|
||||
mb="md"
|
||||
{...form.getInputProps('behaviour.onClickUrl')}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { TablerIcon } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface GenericSecretInputProps {
|
||||
@@ -32,6 +33,7 @@ export const GenericSecretInput = ({
|
||||
const Icon = setIcon;
|
||||
|
||||
const [displayUpdateField, setDisplayUpdateField] = useState<boolean>(false);
|
||||
const { t } = useTranslation(['layout/modals/add-app', 'common']);
|
||||
|
||||
return (
|
||||
<Card withBorder>
|
||||
@@ -43,7 +45,7 @@ export const GenericSecretInput = ({
|
||||
</ThemeIcon>
|
||||
<Stack spacing={0}>
|
||||
<Title className={classes.subtitle} order={6}>
|
||||
{label}
|
||||
{t(label)}
|
||||
</Title>
|
||||
</Stack>
|
||||
</Group>
|
||||
@@ -51,13 +53,13 @@ export const GenericSecretInput = ({
|
||||
<Grid.Col xs={12} md={6}>
|
||||
<Flex gap={10} justify="end" align="end">
|
||||
<Button variant="subtle" color="gray" px="xl">
|
||||
Clear Secret
|
||||
{t('integration.secrets.clear')}
|
||||
</Button>
|
||||
{displayUpdateField === true ? (
|
||||
<PasswordInput placeholder="new secret" width={200} {...props} />
|
||||
) : (
|
||||
<Button onClick={() => setDisplayUpdateField(true)} variant="light">
|
||||
Set Secret
|
||||
{t('integration.secrets.update')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -17,7 +17,7 @@ interface IntegrationSelectorProps {
|
||||
}
|
||||
|
||||
export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
|
||||
// TODO: read this out from integrations dynamically.
|
||||
const data: SelectItem[] = [
|
||||
@@ -76,9 +76,9 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
|
||||
return (
|
||||
<Select
|
||||
label="Integration configuration"
|
||||
description="Treats this app as the selected integration and provides you with per-app configuration"
|
||||
placeholder="Select your desired configuration"
|
||||
label={t('integration.type.label')}
|
||||
description={t('integration.type.description')}
|
||||
placeholder={t('integration.type.placeholder')}
|
||||
itemComponent={SelectItemComponent}
|
||||
data={data}
|
||||
maxDropdownHeight={400}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Alert, Divider, Tabs, Text } from '@mantine/core';
|
||||
import { UseFormReturnType } from '@mantine/form';
|
||||
import { IconAlertTriangle } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Trans, useTranslation } from 'next-i18next';
|
||||
import { AppType } from '../../../../../../types/app';
|
||||
import { IntegrationSelector } from './Components/InputElements/IntegrationSelector';
|
||||
import { IntegrationOptionsRenderer } from './Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer';
|
||||
@@ -11,7 +11,7 @@ interface IntegrationTabProps {
|
||||
}
|
||||
|
||||
export const IntegrationTab = ({ form }: IntegrationTabProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
const hasIntegrationSelected = form.values.integration?.type;
|
||||
|
||||
return (
|
||||
@@ -20,18 +20,19 @@ export const IntegrationTab = ({ form }: IntegrationTabProps) => {
|
||||
|
||||
{hasIntegrationSelected && (
|
||||
<>
|
||||
<Divider label="Integration Configuration" labelPosition="center" mt="xl" mb="md" />
|
||||
<Divider
|
||||
label={t('integration.type.label')}
|
||||
labelPosition="center"
|
||||
mt="xl"
|
||||
mb="md"
|
||||
/>
|
||||
<Text size="sm" color="dimmed" mb="lg">
|
||||
To update a secret, enter a value and click the save button. To remove a secret, use the
|
||||
clear button.
|
||||
{t('integration.secrets.description')}
|
||||
</Text>
|
||||
<IntegrationOptionsRenderer form={form} />
|
||||
<Alert icon={<IconAlertTriangle />} color="yellow">
|
||||
<Text>
|
||||
Please note that Homarr removes secrets from the configuration for security reasons.
|
||||
Thus, you can only either define or unset any credentials. Your credentials act as the
|
||||
main access for your integrations and you should <b>never</b> share them with anybody
|
||||
else. Make sure to <b>store and manage your secrets safely</b>.
|
||||
<Trans i18nKey="integration.secrets.warning" />
|
||||
</Text>
|
||||
</Alert>
|
||||
</>
|
||||
|
||||
@@ -9,12 +9,12 @@ interface NetworkTabProps {
|
||||
}
|
||||
|
||||
export const NetworkTab = ({ form }: NetworkTabProps) => {
|
||||
const { t } = useTranslation('');
|
||||
const { t } = useTranslation('layout/modals/add-app');
|
||||
return (
|
||||
<Tabs.Panel value="network" pt="lg">
|
||||
<Switch
|
||||
label="Enable status checker"
|
||||
description="Sends a simple HTTP / HTTPS request to check if your app is online"
|
||||
label={t('network.statusChecker.label')}
|
||||
description={t('network.statusChecker.description')}
|
||||
mb="md"
|
||||
defaultChecked={form.values.network.enabledStatusChecker}
|
||||
{...form.getInputProps('network.enabledStatusChecker')}
|
||||
@@ -22,8 +22,8 @@ export const NetworkTab = ({ form }: NetworkTabProps) => {
|
||||
{form.values.network.enabledStatusChecker && (
|
||||
<MultiSelect
|
||||
required
|
||||
label="HTTP status codes"
|
||||
description="Determines what response codes are allowed for this app to be 'Online'"
|
||||
label={t('network.statusCodes.label')}
|
||||
description={t('network.statusCodes.description')}
|
||||
data={StatusCodes}
|
||||
clearable
|
||||
searchable
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Card, CardProps } from '@mantine/core';
|
||||
import { ReactNode } from 'react';
|
||||
import { useCardStyles } from '../../layout/useCardStyles';
|
||||
import { useEditModeStore } from '../Views/useEditModeStore';
|
||||
|
||||
interface HomarrCardWrapperProps extends CardProps {
|
||||
children: ReactNode;
|
||||
@@ -11,11 +12,13 @@ export const HomarrCardWrapper = ({ ...props }: HomarrCardWrapperProps) => {
|
||||
cx,
|
||||
classes: { card: cardClass },
|
||||
} = useCardStyles();
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
return (
|
||||
<Card
|
||||
{...props}
|
||||
className={cx(props.className, cardClass)}
|
||||
withBorder
|
||||
style={{ cursor: isEditMode ? 'move' : 'default' }}
|
||||
radius="lg"
|
||||
shadow="md"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Group, Stack } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { useConfigContext } from '../../../config/provider';
|
||||
import { useScreenLargerThan } from '../../../tools/hooks/useScreenLargerThan';
|
||||
import { useScreenSmallerThan } from '../../../tools/hooks/useScreenSmallerThan';
|
||||
import { CategoryType } from '../../../types/category';
|
||||
import { WrapperType } from '../../../types/wrapper';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { DashboardView } from './DashboardView';
|
||||
|
||||
export const DashboardEditView = () => {
|
||||
return <DashboardView />;
|
||||
};
|
||||
export const DashboardEditView = () => <DashboardView />;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, Group, TextInput } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { ContextModalProps } from '@mantine/modals';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import { CategoryType } from '../../../../types/category';
|
||||
@@ -31,15 +32,17 @@ export const CategoryEditModal = ({
|
||||
context.closeModal(id);
|
||||
};
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<TextInput data-autoFocus {...form.getInputProps('name')} label="Name of category" />
|
||||
|
||||
<Group mt="md" grow>
|
||||
<Button onClick={() => context.closeModal(id)} variant="light" color="gray">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
</Group>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -18,11 +18,10 @@ interface WrapperContentProps {
|
||||
};
|
||||
}
|
||||
|
||||
export const WrapperContent = ({ apps, refs, widgets }: WrapperContentProps) => {
|
||||
return (
|
||||
export const WrapperContent = ({ apps, refs, widgets }: WrapperContentProps) => (
|
||||
<>
|
||||
{apps?.map((app) => {
|
||||
const { component: TileComponent, ...tile } = Tiles['app'];
|
||||
const { component: TileComponent, ...tile } = Tiles.app;
|
||||
return (
|
||||
<GridstackTileWrapper
|
||||
id={app.id}
|
||||
@@ -61,4 +60,3 @@ export const WrapperContent = ({ apps, refs, widgets }: WrapperContentProps) =>
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Title,
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
@@ -35,6 +36,7 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
|
||||
const [searchBar, setSearchBar] = useState(defaultLayout?.enabledSearchbar ?? false);
|
||||
|
||||
const { colors, colorScheme } = useMantineTheme();
|
||||
const { t } = useTranslation('settings/common');
|
||||
|
||||
if (!configName) return null;
|
||||
|
||||
@@ -69,7 +71,7 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
|
||||
|
||||
return (
|
||||
<Stack spacing="xs">
|
||||
<Title order={6}>Dashboard layout</Title>
|
||||
<Title order={6}>{t('layout.title')}</Title>
|
||||
|
||||
<Paper px="xs" py={4} withBorder>
|
||||
<Group position="apart">
|
||||
@@ -94,7 +96,7 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
|
||||
{leftSidebar && (
|
||||
<Paper className={classes.secondaryWrapper} p="xs" withBorder>
|
||||
<Flex align="center" justify="center" direction="column">
|
||||
<Text align="center">Sidebar</Text>
|
||||
<Text align="center">{t('layout.sidebar')}</Text>
|
||||
<Text color="dimmed" size="xs" align="center">
|
||||
Only for
|
||||
<br />
|
||||
@@ -106,16 +108,16 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
|
||||
)}
|
||||
|
||||
<Paper className={classes.primaryWrapper} p="xs" withBorder>
|
||||
<Text align="center">Main</Text>
|
||||
<Text align="center">{t('layout.main')}</Text>
|
||||
<Text color="dimmed" size="xs" align="center">
|
||||
Cannot be turned of.
|
||||
{t('cannotturnoff')}
|
||||
</Text>
|
||||
</Paper>
|
||||
|
||||
{rightSidebar && (
|
||||
<Paper className={classes.secondaryWrapper} p="xs" withBorder>
|
||||
<Flex align="center" justify="center" direction="column">
|
||||
<Text align="center">Sidebar</Text>
|
||||
<Text align="center">{t('layout.sidebar')}</Text>
|
||||
<Text color="dimmed" size="xs" align="center">
|
||||
Only for
|
||||
<br />
|
||||
@@ -129,29 +131,29 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
|
||||
|
||||
<Stack spacing="xs">
|
||||
<Checkbox
|
||||
label="Enable left sidebar"
|
||||
description="Optional. Can be used for apps and integrations only"
|
||||
label={t('layout.enablelsidebar')}
|
||||
description={t('layout.enablelsidebardesc')}
|
||||
checked={leftSidebar}
|
||||
onChange={(ev) => handleChange('enabledLeftSidebar', ev, setLeftSidebar)}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Enable right sidebar"
|
||||
description="Optional. Can be used for apps and integrations only"
|
||||
label={t('layout.enablersidebar')}
|
||||
description={t('layout.enablersidebardesc')}
|
||||
checked={rightSidebar}
|
||||
onChange={(ev) => handleChange('enabledRightSidebar', ev, setRightSidebar)}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Enable search bar"
|
||||
label={t('layout.enablesearchbar')}
|
||||
checked={searchBar}
|
||||
onChange={(ev) => handleChange('enabledSearchbar', ev, setSearchBar)}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Enable docker"
|
||||
label={t('layout.enabledocker')}
|
||||
checked={docker}
|
||||
onChange={(ev) => handleChange('enabledDocker', ev, setDocker)}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Enable pings"
|
||||
label={t('layout.enableping')}
|
||||
checked={ping}
|
||||
onChange={(ev) => handleChange('enabledPing', ev, setPing)}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IconApps } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export const AddElementAction = () => {
|
||||
const { t } = useTranslation('layout/add-service-app-shelf');
|
||||
const { t } = useTranslation('layout/element-selector/selector');
|
||||
|
||||
return (
|
||||
<Tooltip withinPortal label={t('actionIcon.tooltip')}>
|
||||
@@ -16,7 +16,7 @@ export const AddElementAction = () => {
|
||||
onClick={() =>
|
||||
openContextModal({
|
||||
modal: 'selectElement',
|
||||
title: 'Add an element to your dashboard',
|
||||
title: t('modal.title'),
|
||||
size: 'xl',
|
||||
innerProps: {},
|
||||
})
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { ActionIcon, Menu, Tooltip } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconInfoCircle, IconMenu2, IconSettings } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { AboutModal } from '../../About/AboutModal';
|
||||
import { SettingsDrawer } from '../../Settings/SettingsDrawer';
|
||||
import { ColorSchemeSwitch } from './SettingsMenu/ColorSchemeSwitch';
|
||||
|
||||
export const SettingsMenu = () => {
|
||||
const [drawerOpened, drawer] = useDisclosure(false);
|
||||
const { t } = useTranslation('common');
|
||||
const [aboutModalOpened, aboutModal] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
@@ -22,7 +24,7 @@ export const SettingsMenu = () => {
|
||||
<ColorSchemeSwitch />
|
||||
<Menu.Divider />
|
||||
<Menu.Item icon={<IconSettings strokeWidth={1.2} size={18} />} onClick={drawer.open}>
|
||||
Homarr Settings
|
||||
{t('sections.settings')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<IconInfoCircle strokeWidth={1.2} size={18} />}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
export const dashboardNamespaces = [
|
||||
'common',
|
||||
'layout/app-shelf',
|
||||
'layout/add-service-app-shelf',
|
||||
'layout/app-shelf-menu',
|
||||
'layout/tools',
|
||||
'layout/element-selector/selector',
|
||||
'layout/modals/add-app',
|
||||
'layout/header/actions/toggle-edit-mode',
|
||||
'settings/common',
|
||||
'settings/general/theme-selector',
|
||||
|
||||
@@ -90,16 +90,16 @@ export const integrationFieldDefinitions: {
|
||||
apiKey: {
|
||||
type: 'private',
|
||||
icon: IconKey,
|
||||
label: 'API Key',
|
||||
label: 'common:secrets.apiKey',
|
||||
},
|
||||
username: {
|
||||
type: 'public',
|
||||
icon: IconUser,
|
||||
label: 'Username',
|
||||
label: 'common:secrets.username',
|
||||
},
|
||||
password: {
|
||||
type: 'private',
|
||||
icon: IconPassword,
|
||||
label: 'Password',
|
||||
label: 'common:secrets.password',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4536,7 +4536,7 @@ __metadata:
|
||||
|
||||
"fsevents@patch:fsevents@^2.3.2#~builtin<compat/fsevents>, fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
|
||||
version: 2.3.2
|
||||
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=df0bf1"
|
||||
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=18f3a7"
|
||||
dependencies:
|
||||
node-gyp: latest
|
||||
conditions: os=darwin
|
||||
@@ -7489,7 +7489,7 @@ __metadata:
|
||||
|
||||
"resolve@patch:resolve@^1.19.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.0#~builtin<compat/resolve>":
|
||||
version: 1.22.1
|
||||
resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=c3c19d"
|
||||
resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"
|
||||
dependencies:
|
||||
is-core-module: ^2.9.0
|
||||
path-parse: ^1.0.7
|
||||
@@ -7502,7 +7502,7 @@ __metadata:
|
||||
|
||||
"resolve@patch:resolve@^2.0.0-next.3#~builtin<compat/resolve>":
|
||||
version: 2.0.0-next.4
|
||||
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin<compat/resolve>::version=2.0.0-next.4&hash=c3c19d"
|
||||
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin<compat/resolve>::version=2.0.0-next.4&hash=07638b"
|
||||
dependencies:
|
||||
is-core-module: ^2.9.0
|
||||
path-parse: ^1.0.7
|
||||
@@ -8338,7 +8338,7 @@ __metadata:
|
||||
|
||||
"typescript@patch:typescript@^4.7.4#~builtin<compat/typescript>":
|
||||
version: 4.9.4
|
||||
resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin<compat/typescript>::version=4.9.4&hash=d73830"
|
||||
resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin<compat/typescript>::version=4.9.4&hash=7ad353"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
|
||||
Reference in New Issue
Block a user