mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-11 16:05:47 +01:00
🚧 wip migrate to next-i18n
This commit is contained in:
@@ -22,25 +22,26 @@ import { IconApps } from '@tabler/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { tryMatchPort, ServiceTypeList, StatusCodes } from '../../tools/types';
|
||||
import Tip from '../layout/Tip';
|
||||
|
||||
export function AddItemShelfButton(props: any) {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { t } = useTranslation('layout/add-service-app-shelf');
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
size="xl"
|
||||
radius="md"
|
||||
title={<Title order={3}>{t('layout.header.addService.modal.title')}</Title>}
|
||||
title={<Title order={3}>{t('modal.title')}</Title>}
|
||||
opened={props.opened || opened}
|
||||
onClose={() => setOpened(false)}
|
||||
>
|
||||
<AddAppShelfItemForm setOpened={setOpened} />
|
||||
</Modal>
|
||||
<Tooltip withinPortal label={t('layout.header.addService.actionIcon.tooltip')}>
|
||||
<Tooltip withinPortal label={t('actionIcon.tooltip')}>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
radius="md"
|
||||
@@ -85,6 +86,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
const { setOpened } = props;
|
||||
const { config, setConfig } = useConfig();
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const { t } = useTranslation('layout/add-service-app-shelf');
|
||||
|
||||
// Extract all the categories from the services in config
|
||||
const InitialCategories = config.services.reduce((acc, cur) => {
|
||||
@@ -121,13 +123,13 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
try {
|
||||
const _isValid = new URL(value);
|
||||
} catch (e) {
|
||||
return t('layout.header.addService.modal.form.validation.invalidUrl');
|
||||
return t('modal.form.validation.invalidUrl');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
status: (value: string[]) => {
|
||||
if (!value.length) {
|
||||
return t('layout.header.addService.modal.form.validation.noStatusCodeSelected');
|
||||
return t('modal.form.validation.noStatusCodeSelected');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -204,62 +206,48 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
>
|
||||
<Tabs defaultValue="Options">
|
||||
<Tabs.List grow>
|
||||
<Tabs.Tab value="Options">
|
||||
{t('layout.header.addService.modal.tabs.options.title')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value="Advanced Options">
|
||||
{t('layout.header.addService.modal.tabs.advancedOptions.title')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value="Options">{t('modal.tabs.options.title')}</Tabs.Tab>
|
||||
<Tabs.Tab value="Advanced Options">{t('modal.tabs.advancedOptions.title')}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="Options">
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t('layout.header.addService.modal.tabs.options.form.serviceName.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.serviceName.placeholder'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.serviceName.label')}
|
||||
placeholder={t('modal.tabs.options.form.serviceName.placeholder')}
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label={t('layout.header.addService.modal.tabs.options.form.iconUrl.label')}
|
||||
label={t('modal.tabs.options.form.iconUrl.label')}
|
||||
placeholder={DEFAULT_ICON}
|
||||
{...form.getInputProps('icon')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label={t('layout.header.addService.modal.tabs.options.form.serviceUrl.label')}
|
||||
label={t('modal.tabs.options.form.serviceUrl.label')}
|
||||
placeholder="http://localhost:7575"
|
||||
{...form.getInputProps('url')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('layout.header.addService.modal.tabs.options.form.onClickUrl.label')}
|
||||
label={t('modal.tabs.options.form.onClickUrl.label')}
|
||||
placeholder="http://sonarr.example.com"
|
||||
{...form.getInputProps('openedUrl')}
|
||||
/>
|
||||
<Select
|
||||
label={t('layout.header.addService.modal.tabs.options.form.serviceType.label')}
|
||||
defaultValue={t(
|
||||
'layout.header.addService.modal.tabs.options.form.serviceType.defaultValue'
|
||||
)}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.serviceType.placeholder'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.serviceType.label')}
|
||||
defaultValue={t('modal.tabs.options.form.serviceType.defaultValue')}
|
||||
placeholder={t('modal.tabs.options.form.serviceType.placeholder')}
|
||||
required
|
||||
searchable
|
||||
data={ServiceTypeList}
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
<Select
|
||||
label={t('layout.header.addService.modal.tabs.options.form.category.label')}
|
||||
label={t('modal.tabs.options.form.category.label')}
|
||||
data={categories}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.category.placeholder'
|
||||
)}
|
||||
nothingFound={t(
|
||||
'layout.header.addService.modal.tabs.options.form.category.nothingFound'
|
||||
)}
|
||||
placeholder={t('modal.tabs.options.form.category.placeholder')}
|
||||
nothingFound={t('modal.tabs.options.form.category.nothingFound')}
|
||||
searchable
|
||||
clearable
|
||||
creatable
|
||||
@@ -269,7 +257,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
return item;
|
||||
}}
|
||||
getCreateLabel={(query) =>
|
||||
t('layout.header.addService.modal.tabs.options.form.category.createLabel', {
|
||||
t('modal.tabs.options.form.category.createLabel', {
|
||||
query,
|
||||
})
|
||||
}
|
||||
@@ -285,36 +273,26 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
<>
|
||||
<TextInput
|
||||
required
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.apiKey.label'
|
||||
)}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.apiKey.placeholder'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.apiKey.label')}
|
||||
placeholder={t('modal.tabs.options.form.integrations.apiKey.placeholder')}
|
||||
value={form.values.apiKey}
|
||||
onChange={(event) => {
|
||||
form.setFieldValue('apiKey', event.currentTarget.value);
|
||||
}}
|
||||
error={
|
||||
form.errors.apiKey &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.apiKey.validation.noKey'
|
||||
)
|
||||
t('modal.tabs.options.form.integrations.apiKey.validation.noKey')
|
||||
}
|
||||
/>
|
||||
<Tip>
|
||||
{t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.apiKey.tip.text'
|
||||
)}{' '}
|
||||
{t('modal.tabs.options.form.integrations.apiKey.tip.text')}{' '}
|
||||
<Anchor
|
||||
target="_blank"
|
||||
weight="bold"
|
||||
style={{ fontStyle: 'inherit', fontSize: 'inherit' }}
|
||||
href={`${hostname}/settings/general`}
|
||||
>
|
||||
{t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.apiKey.tip.link'
|
||||
)}
|
||||
{t('modal.tabs.options.form.integrations.apiKey.tip.link')}
|
||||
</Anchor>
|
||||
</Tip>
|
||||
</>
|
||||
@@ -323,11 +301,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
<>
|
||||
<TextInput
|
||||
required
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.username.label'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.qBittorrent.username.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.username.placeholder'
|
||||
'modal.tabs.options.form.integrations.qBittorrent.username.placeholder'
|
||||
)}
|
||||
value={form.values.username}
|
||||
onChange={(event) => {
|
||||
@@ -336,17 +312,15 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
error={
|
||||
form.errors.username &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.username.validation.invalidUsername'
|
||||
'modal.tabs.options.form.integrations.qBittorrent.username.validation.invalidUsername'
|
||||
)
|
||||
}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.password.label'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.qBittorrent.password.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.password.placeholder'
|
||||
'modal.tabs.options.form.integrations.qBittorrent.password.placeholder'
|
||||
)}
|
||||
value={form.values.password}
|
||||
onChange={(event) => {
|
||||
@@ -355,7 +329,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
error={
|
||||
form.errors.password &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.password.validation.invalidPassword'
|
||||
'modal.tabs.options.form.integrations.qBittorrent.password.validation.invalidPassword'
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -364,11 +338,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
{form.values.type === 'Deluge' && (
|
||||
<>
|
||||
<PasswordInput
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.deluge.password.label'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.deluge.password.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.deluge.password.placeholder'
|
||||
'modal.tabs.options.form.integrations.deluge.password.placeholder'
|
||||
)}
|
||||
value={form.values.password}
|
||||
onChange={(event) => {
|
||||
@@ -377,7 +349,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
error={
|
||||
form.errors.password &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.deluge.password.validation.invalidPassword'
|
||||
'modal.tabs.options.form.integrations.deluge.password.validation.invalidPassword'
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -386,11 +358,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
{form.values.type === 'Transmission' && (
|
||||
<>
|
||||
<TextInput
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.username.label'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.transmission.username.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.username.placeholder'
|
||||
'modal.tabs.options.form.integrations.transmission.username.placeholder'
|
||||
)}
|
||||
value={form.values.username}
|
||||
onChange={(event) => {
|
||||
@@ -399,16 +369,14 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
error={
|
||||
form.errors.username &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.username.validation.invalidUsername'
|
||||
'modal.tabs.options.form.integrations.transmission.username.validation.invalidUsername'
|
||||
)
|
||||
}
|
||||
/>
|
||||
<PasswordInput
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.password.label'
|
||||
)}
|
||||
label={t('modal.tabs.options.form.integrations.transmission.password.label')}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.password.placeholder'
|
||||
'modal.tabs.options.form.integrations.transmission.password.placeholder'
|
||||
)}
|
||||
value={form.values.password}
|
||||
onChange={(event) => {
|
||||
@@ -417,7 +385,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
error={
|
||||
form.errors.password &&
|
||||
t(
|
||||
'layout.header.addService.modal.tabs.options.form.integrations.transmission.password.validation.invalidPassword'
|
||||
'modal.tabs.options.form.integrations.transmission.password.validation.invalidPassword'
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -425,32 +393,24 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
)}
|
||||
</Stack>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={t('layout.header.addService.modal.tabs.advancedOptions.title')}>
|
||||
<Tabs.Panel value={t('modal.tabs.advancedOptions.title')}>
|
||||
<Stack>
|
||||
<MultiSelect
|
||||
required
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.advancedOptions.form.httpStatusCodes.label'
|
||||
)}
|
||||
label={t('modal.tabs.advancedOptions.form.httpStatusCodes.label')}
|
||||
data={StatusCodes}
|
||||
placeholder={t(
|
||||
'layout.header.addService.modal.tabs.advancedOptions.form.httpStatusCodes.placeholder'
|
||||
)}
|
||||
placeholder={t('modal.tabs.advancedOptions.form.httpStatusCodes.placeholder')}
|
||||
clearButtonLabel={t(
|
||||
'layout.header.addService.modal.tabs.advancedOptions.form.httpStatusCodes.clearButtonLabel'
|
||||
)}
|
||||
nothingFound={t(
|
||||
'layout.header.addService.modal.tabs.advancedOptions.form.httpStatusCodes.nothingFound'
|
||||
'modal.tabs.advancedOptions.form.httpStatusCodes.clearButtonLabel'
|
||||
)}
|
||||
nothingFound={t('modal.tabs.advancedOptions.form.httpStatusCodes.nothingFound')}
|
||||
defaultValue={['200']}
|
||||
clearable
|
||||
searchable
|
||||
{...form.getInputProps('status')}
|
||||
/>
|
||||
<Switch
|
||||
label={t(
|
||||
'layout.header.addService.modal.tabs.advancedOptions.form.openServiceInNewTab.label'
|
||||
)}
|
||||
label={t('modal.tabs.advancedOptions.form.openServiceInNewTab.label')}
|
||||
defaultChecked={form.values.newTab}
|
||||
{...form.getInputProps('newTab')}
|
||||
/>
|
||||
@@ -459,8 +419,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
||||
</Tabs>
|
||||
<Group grow position="center" mt="xl">
|
||||
<Button type="submit">
|
||||
{props.message ??
|
||||
t('layout.header.addService.modal.tabs.advancedOptions.form.buttons.submit.content')}
|
||||
{props.message ?? t('modal.tabs.advancedOptions.form.buttons.submit.content')}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@dnd-kit/core';
|
||||
import { arrayMove, SortableContext } from '@dnd-kit/sortable';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem';
|
||||
@@ -36,6 +37,8 @@ const AppShelf = (props: any) => {
|
||||
const [activeId, setActiveId] = useState(null);
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
const { t } = useTranslation('layout/app-shelf');
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint: {
|
||||
@@ -147,13 +150,13 @@ const AppShelf = (props: any) => {
|
||||
{/* Return the item for all services without category */}
|
||||
{noCategory && noCategory.length > 0 ? (
|
||||
<Accordion.Item key="Other" value="Other">
|
||||
<Accordion.Control>Other</Accordion.Control>
|
||||
<Accordion.Control>{t('accordions.others.text')}</Accordion.Control>
|
||||
<Accordion.Panel>{getItems()}</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
) : null}
|
||||
{downloadEnabled ? (
|
||||
<Accordion.Item key="Downloads" value="Your downloads">
|
||||
<Accordion.Control>Your downloads</Accordion.Control>
|
||||
<Accordion.Control>{t('accordions.downloads.text')}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Paper
|
||||
p="lg"
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { createStyles, Switch, Group, useMantineColorScheme, Kbd } from '@mantine/core';
|
||||
import { IconSun as Sun, IconMoonStars as MoonStars } from '@tabler/icons';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
root: {
|
||||
@@ -34,6 +34,7 @@ export function ColorSchemeSwitch() {
|
||||
const { config } = useConfig();
|
||||
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||
const { classes, cx } = useStyles();
|
||||
const { t } = useTranslation('settings/general/theme-selector');
|
||||
|
||||
return (
|
||||
<Group>
|
||||
@@ -42,8 +43,8 @@ export function ColorSchemeSwitch() {
|
||||
<MoonStars className={cx(classes.icon, classes.iconDark)} size={18} />
|
||||
<Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" />
|
||||
</div>
|
||||
{t('settings.tabs.common.settings.colorScheme.label', {
|
||||
scheme: colorScheme === 'dark' ? 'light' : 'dark',
|
||||
{t('label', {
|
||||
theme: colorScheme === 'dark' ? 'light' : 'dark',
|
||||
})}
|
||||
<Group spacing={2}>
|
||||
<Kbd>Ctrl</Kbd>+<Kbd>J</Kbd>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Center, Loader, Select, Tooltip } from '@mantine/core';
|
||||
import { setCookie } from 'cookies-next';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
@@ -8,6 +8,8 @@ export default function ConfigChanger() {
|
||||
const { config, loadConfig, setConfig, getConfigs } = useConfig();
|
||||
const [configList, setConfigList] = useState<string[]>([]);
|
||||
const [value, setValue] = useState(config.name);
|
||||
const { t } = useTranslation('settings/general/config-changer');
|
||||
|
||||
useEffect(() => {
|
||||
getConfigs().then((configs) => setConfigList(configs));
|
||||
}, [config]);
|
||||
@@ -24,7 +26,7 @@ export default function ConfigChanger() {
|
||||
// return <Select data={[{ value: '1', label: '1' },]} onChange={(e) => console.log(e)} value="1" />;
|
||||
return (
|
||||
<Select
|
||||
label={t('settings.tabs.common.settings.configChanger.configSelect.label')}
|
||||
label={t('configSelect.label')}
|
||||
value={value}
|
||||
defaultValue={config.name}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { showNotification } from '@mantine/notifications';
|
||||
import axios from 'axios';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
IconCheck as Check,
|
||||
IconDownload as Download,
|
||||
@@ -12,11 +13,11 @@ import {
|
||||
IconX as X,
|
||||
} from '@tabler/icons';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export default function SaveConfigComponent(props: any) {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { config, setConfig } = useConfig();
|
||||
const { t } = useTranslation('settings/general/config-changer');
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
configName: config.name,
|
||||
@@ -29,12 +30,7 @@ export default function SaveConfigComponent(props: any) {
|
||||
}
|
||||
return (
|
||||
<Group spacing="xs">
|
||||
<Modal
|
||||
radius="md"
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
title={t('settings.tabs.common.settings.configChanger.modal.title')}
|
||||
>
|
||||
<Modal radius="md" opened={opened} onClose={() => setOpened(false)} title={t('modal.title')}>
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
setConfig({ ...config, name: values.configName });
|
||||
@@ -51,21 +47,17 @@ export default function SaveConfigComponent(props: any) {
|
||||
>
|
||||
<TextInput
|
||||
required
|
||||
label={t('settings.tabs.common.settings.configChanger.modal.form.configName.label')}
|
||||
placeholder={t(
|
||||
'settings.tabs.common.settings.configChanger.modal.form.configName.placeholder'
|
||||
)}
|
||||
label={t('modal.form.configName.label')}
|
||||
placeholder={t('modal.form.configName.placeholder')}
|
||||
{...form.getInputProps('configName')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button type="submit">
|
||||
{t('settings.tabs.common.settings.configChanger.modal.form.buttons.submit')}
|
||||
</Button>
|
||||
<Button type="submit">{t('modal.form.buttons.submit')}</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Modal>
|
||||
<Button size="xs" leftIcon={<Download />} variant="outline" onClick={onClick}>
|
||||
{t('settings.tabs.common.settings.configChanger.buttons.download')}
|
||||
{t('buttons.download')}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
@@ -76,39 +68,31 @@ export default function SaveConfigComponent(props: any) {
|
||||
.delete(`/api/configs/${config.name}`)
|
||||
.then(() => {
|
||||
showNotification({
|
||||
title: t(
|
||||
'settings.tabs.common.settings.configChanger.buttons.delete.deleted.title'
|
||||
),
|
||||
title: t('buttons.delete.deleted.title'),
|
||||
icon: <Check />,
|
||||
color: 'green',
|
||||
autoClose: 1500,
|
||||
radius: 'md',
|
||||
message: t(
|
||||
'settings.tabs.common.settings.configChanger.buttons.delete.deleted.message'
|
||||
),
|
||||
message: t('buttons.delete.deleted.message'),
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
showNotification({
|
||||
title: t(
|
||||
'settings.tabs.common.settings.configChanger.buttons.delete.deleteFailed.title'
|
||||
),
|
||||
title: t('buttons.delete.deleteFailed.title'),
|
||||
icon: <X />,
|
||||
color: 'red',
|
||||
autoClose: 1500,
|
||||
radius: 'md',
|
||||
message: t(
|
||||
'settings.tabs.common.settings.configChanger.buttons.delete.deleteFailed.message'
|
||||
),
|
||||
message: t('buttons.delete.deleteFailed.message'),
|
||||
});
|
||||
});
|
||||
setConfig({ ...config, name: 'default' });
|
||||
}}
|
||||
>
|
||||
{t('settings.tabs.common.settings.configChanger.buttons.delete.text')}
|
||||
{t('buttons.delete.text')}
|
||||
</Button>
|
||||
<Button size="xs" leftIcon={<Plus />} variant="outline" onClick={() => setOpened(true)}>
|
||||
{t('settings.tabs.common.settings.configChanger.buttons.saveCopy')}
|
||||
{t('buttons.saveCopy')}
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { TextInput, Button, Stack } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { ColorSelector } from './ColorSelector';
|
||||
import { OpacitySelector } from './OpacitySelector';
|
||||
import { AppCardWidthSelector } from './AppCardWidthSelector';
|
||||
import { ShadeSelector } from './ShadeSelector';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export default function TitleChanger() {
|
||||
const { config, setConfig } = useConfig();
|
||||
const { t } = useTranslation('settings/customization/page-appearance');
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
@@ -42,26 +43,26 @@ export default function TitleChanger() {
|
||||
<form onSubmit={form.onSubmit((values) => saveChanges(values))}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.pageTitle.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.pageTitle.placeholder')}
|
||||
label={t('pageTitle.label')}
|
||||
placeholder={t('pageTitle.placeholder')}
|
||||
{...form.getInputProps('title')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.logo.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.logo.placeholder')}
|
||||
label={t('logo.label')}
|
||||
placeholder={t('logo.placeholder')}
|
||||
{...form.getInputProps('logo')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.favicon.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.favicon.placeholder')}
|
||||
label={t('favicon.label')}
|
||||
placeholder={t('favicon.placeholder')}
|
||||
{...form.getInputProps('favicon')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.background.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.background.placeholder')}
|
||||
label={t('background.label')}
|
||||
placeholder={t('background.placeholder')}
|
||||
{...form.getInputProps('background')}
|
||||
/>
|
||||
<Button type="submit">{t('settings.tabs.customizations.settings.buttons.submit')}</Button>
|
||||
<Button type="submit">{t('buttons.submit')}</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
<ColorSelector type="primary" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ColorSwatch, Grid, Group, Popover, Text, useMantineTheme } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { useColorTheme } from '../../tools/color';
|
||||
import { t } from 'i18next';
|
||||
|
||||
interface ColorControlProps {
|
||||
type: string;
|
||||
@@ -11,8 +11,8 @@ interface ColorControlProps {
|
||||
export function ColorSelector({ type }: ColorControlProps) {
|
||||
const { config, setConfig } = useConfig();
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const { primaryColor, secondaryColor, setPrimaryColor, setSecondaryColor } = useColorTheme();
|
||||
const { t } = useTranslation('settings/customization/color-selector');
|
||||
|
||||
const theme = useMantineTheme();
|
||||
const colors = Object.keys(theme.colors).map((color) => ({
|
||||
@@ -84,7 +84,7 @@ export function ColorSelector({ type }: ColorControlProps) {
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Text>
|
||||
{t('settings.tabs.customizations.settings.colorSelector.suffix', {
|
||||
{t('suffix', {
|
||||
color: type[0].toUpperCase() + type.slice(1),
|
||||
})}
|
||||
</Text>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Text, SegmentedControl, TextInput, Stack } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { ColorSchemeSwitch } from '../ColorSchemeToggle/ColorSchemeSwitch';
|
||||
import { WidgetsPositionSwitch } from '../WidgetsPositionSwitch/WidgetsPositionSwitch';
|
||||
@@ -7,11 +8,14 @@ import ConfigChanger from '../Config/ConfigChanger';
|
||||
import SaveConfigComponent from '../Config/SaveConfig';
|
||||
import ModuleEnabler from './ModuleEnabler';
|
||||
import Tip from '../layout/Tip';
|
||||
import { t } from 'i18next';
|
||||
import LanguageSwitch from './LanguageSwitch';
|
||||
|
||||
export default function CommonSettings(args: any) {
|
||||
const { config, setConfig } = useConfig();
|
||||
const { t } = useTranslation([
|
||||
'settings/general/search-engine',
|
||||
'settings/general/config-changer',
|
||||
]);
|
||||
|
||||
const matches = [
|
||||
{ label: 'Google', value: 'https://google.com/search?q=' },
|
||||
@@ -28,12 +32,12 @@ export default function CommonSettings(args: any) {
|
||||
return (
|
||||
<Stack mb="md" mr="sm">
|
||||
<Stack spacing={0} mt="xs">
|
||||
<Text>{t('settings.tabs.common.settings.searchEngine.title')}</Text>
|
||||
<Tip>{t('settings.tabs.common.settings.searchEngine.tips.generalTip')}</Tip>
|
||||
<Text>{t('title')}</Text>
|
||||
<Tip>{t('tips.generalTip')}</Tip>
|
||||
<SegmentedControl
|
||||
fullWidth
|
||||
mb="sm"
|
||||
title={t('settings.tabs.common.settings.searchEngine.title')}
|
||||
title={t('title')}
|
||||
value={
|
||||
// Match config.settings.searchUrl with a key in the matches array
|
||||
searchUrl
|
||||
@@ -55,10 +59,10 @@ export default function CommonSettings(args: any) {
|
||||
/>
|
||||
{searchUrl === 'Custom' && (
|
||||
<>
|
||||
<Tip>{t('settings.tabs.common.settings.searchEngine.tips.placeholderTip')}</Tip>
|
||||
<Tip>{t('tips.placeholderTip')}</Tip>
|
||||
<TextInput
|
||||
label={t('settings.tabs.common.settings.searchEngine.customEngine.label')}
|
||||
placeholder={t('settings.tabs.common.settings.searchEngine.customEngine.placeholder')}
|
||||
label={t('customEngine.label')}
|
||||
placeholder={t('customEngine.placeholder')}
|
||||
value={customSearchUrl}
|
||||
onChange={(event) => {
|
||||
setCustomSearchUrl(event.currentTarget.value);
|
||||
@@ -80,7 +84,7 @@ export default function CommonSettings(args: any) {
|
||||
<LanguageSwitch />
|
||||
<ConfigChanger />
|
||||
<SaveConfigComponent />
|
||||
<Tip>{t('settings.tabs.common.settings.configTip')}</Tip>
|
||||
<Tip>{t('configTip')}</Tip>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Group, ActionIcon, Anchor, Text } from '@mantine/core';
|
||||
import { IconBrandDiscord, IconBrandGithub } from '@tabler/icons';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { CURRENT_VERSION } from '../../../data/constants';
|
||||
|
||||
export default function Credits(props: any) {
|
||||
const { t } = useTranslation('settings/common');
|
||||
|
||||
return (
|
||||
<Group position="center" mt="xs">
|
||||
<Group spacing={0}>
|
||||
@@ -29,7 +31,7 @@ export default function Credits(props: any) {
|
||||
color: 'gray',
|
||||
}}
|
||||
>
|
||||
{t('settings.credits.madeWithLove')}
|
||||
{t('credits.madeWithLove')}
|
||||
<Anchor
|
||||
href="https://github.com/ajnart"
|
||||
style={{ color: 'inherit', fontStyle: 'inherit', fontSize: 'inherit' }}
|
||||
|
||||
@@ -3,27 +3,26 @@ import { showNotification } from '@mantine/notifications';
|
||||
import { IconLanguage } from '@tabler/icons';
|
||||
|
||||
import { forwardRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { convertCodeToName } from '../../translations/i18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export default function LanguageSwitch() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { language, languages, changeLanguage } = i18n;
|
||||
const { t, i18n } = useTranslation('settings/general/internationalization');
|
||||
/*const { language, languages, changeLanguage } = i18n;
|
||||
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<string | null>(language);
|
||||
|
||||
const data = languages.map((language) => ({
|
||||
image: `https://countryflagsapi.com/png/${language.split('-').pop()}`,
|
||||
label: convertCodeToName(language),
|
||||
label: 'JA',
|
||||
value: language,
|
||||
}));
|
||||
}));*/
|
||||
|
||||
const onChangeSelect = (value: string) => {
|
||||
setSelectedLanguage(value);
|
||||
//setSelectedLanguage(value);
|
||||
|
||||
const languageName = convertCodeToName(value);
|
||||
const languageName = 'JA IS HALZ SCHEISSE NE';
|
||||
|
||||
changeLanguage(value)
|
||||
/*changeLanguage(value)
|
||||
.then(() => {
|
||||
showNotification({
|
||||
title: 'Language changed',
|
||||
@@ -39,20 +38,27 @@ export default function LanguageSwitch() {
|
||||
color: 'red',
|
||||
autoClose: 5000,
|
||||
});
|
||||
});
|
||||
});*/
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Select
|
||||
icon={<IconLanguage size={18} />}
|
||||
label={t('settings.tabs.common.settings.language.title')}
|
||||
data={data}
|
||||
label={t('label')}
|
||||
data={[
|
||||
{
|
||||
value: 'uwu',
|
||||
label: 'asdf',
|
||||
},
|
||||
]}
|
||||
itemComponent={SelectItem}
|
||||
nothingFound="Nothing found"
|
||||
onChange={onChangeSelect}
|
||||
/*
|
||||
value={selectedLanguage}
|
||||
defaultValue={language}
|
||||
*/
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { Checkbox, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import * as Modules from '../../modules';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
export default function ModuleEnabler(props: any) {
|
||||
const { config, setConfig } = useConfig();
|
||||
const { t } = useTranslation('settings/general/module-enabler');
|
||||
const modules = Object.values(Modules).map((module) => module);
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={4}>{t('settings.tabs.common.settings.moduleEnabler.title')}</Title>
|
||||
<Title order={4}>{t('title')}</Title>
|
||||
<SimpleGrid cols={3} spacing="xs">
|
||||
{modules.map((module) => (
|
||||
<Checkbox
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Text, Slider, Stack } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export function OpacitySelector() {
|
||||
const { config, setConfig } = useConfig();
|
||||
const { t } = useTranslation('settings/customization/shade-selector');
|
||||
|
||||
const MARKS = [
|
||||
{ value: 10, label: '10' },
|
||||
@@ -31,7 +32,7 @@ export function OpacitySelector() {
|
||||
|
||||
return (
|
||||
<Stack spacing="xs">
|
||||
<Text>{t('settings.tabs.customizations.settings.opacitySelector.label')}</Text>
|
||||
<Text>{t('label')}</Text>
|
||||
<Slider
|
||||
defaultValue={config.settings.appOpacity || 100}
|
||||
step={10}
|
||||
|
||||
@@ -2,18 +2,20 @@ import { ActionIcon, Title, Tooltip, Drawer, Tabs, ScrollArea } from '@mantine/c
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
import { IconSettings } from '@tabler/icons';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import AdvancedSettings from './AdvancedSettings';
|
||||
import CommonSettings from './CommonSettings';
|
||||
import Credits from './Credits';
|
||||
|
||||
function SettingsMenu(props: any) {
|
||||
const { t } = useTranslation('settings/common');
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="Common">
|
||||
<Tabs.List grow>
|
||||
<Tabs.Tab value="Common">{t('settings.tabs.common.title')}</Tabs.Tab>
|
||||
<Tabs.Tab value="Customizations">{t('settings.tabs.customizations.title')}</Tabs.Tab>
|
||||
<Tabs.Tab value="Common">{t('tabs.common')}</Tabs.Tab>
|
||||
<Tabs.Tab value="Customizations">{t('tabs.customizations')}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel data-autofocus value="Common">
|
||||
<ScrollArea style={{ height: '78vh' }} offsetScrollbars>
|
||||
@@ -31,22 +33,24 @@ function SettingsMenu(props: any) {
|
||||
|
||||
export function SettingsMenuButton(props: any) {
|
||||
useHotkeys([['ctrl+L', () => setOpened(!opened)]]);
|
||||
const { t } = useTranslation('settings/common');
|
||||
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
size="xl"
|
||||
padding="lg"
|
||||
position="right"
|
||||
title={<Title order={5}>{t('settings.title')}</Title>}
|
||||
title={<Title order={5}>{t('title')}</Title>}
|
||||
opened={props.opened || opened}
|
||||
onClose={() => setOpened(false)}
|
||||
>
|
||||
<SettingsMenu />
|
||||
<Credits />
|
||||
</Drawer>
|
||||
<Tooltip label={t('settings.tooltip')}>
|
||||
<Tooltip label={t('tooltip')}>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
radius="md"
|
||||
|
||||
@@ -9,13 +9,14 @@ import {
|
||||
Stack,
|
||||
Grid,
|
||||
} from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { useColorTheme } from '../../tools/color';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export function ShadeSelector() {
|
||||
const { config, setConfig } = useConfig();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { t } = useTranslation('settings/general/shade-selector');
|
||||
|
||||
const { primaryColor, secondaryColor, primaryShade, setPrimaryShade } = useColorTheme();
|
||||
|
||||
@@ -95,7 +96,7 @@ export function ShadeSelector() {
|
||||
</Stack>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Text>{t('settings.tabs.customizations.settings.shadeSelector.label')}</Text>
|
||||
<Text>{t('label')}</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { createStyles, Switch, Group } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
root: {
|
||||
@@ -34,6 +34,7 @@ export function WidgetsPositionSwitch() {
|
||||
const { classes, cx } = useStyles();
|
||||
const defaultPosition = config?.settings?.widgetPosition || 'right';
|
||||
const [widgetPosition, setWidgetPosition] = useState(defaultPosition);
|
||||
const { t } = useTranslation('settings/general/widget-positions');
|
||||
const toggleWidgetPosition = () => {
|
||||
const position = widgetPosition === 'right' ? 'left' : 'right';
|
||||
setWidgetPosition(position);
|
||||
@@ -55,7 +56,7 @@ export function WidgetsPositionSwitch() {
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
{t('settings.tabs.common.settings.widgetsPositionSwitch.label')}
|
||||
{t('label')}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user