mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
P UI: Basic UI & Auth setup (#5207)
* update deps * remove @mantine/rte * add icons again * Create dependency-review.yml * Create scan.yml * Create sonar-project.properties * add option to use sections and refactro * translate error messages * remove unneeded vars * move function code * move data inside * add global section * add plugin section * use translated section titles * add translation strings * rename scan action * add user settings * use ordered data * fix settings url * use debounced value for strings (not choices!) * rename contex to context * move i18n provider up * move theme options into seperate context/ component * renmae statrtup vars * move translations out * reactivate sentry * move i18n provider to seperate context * move langauge state completly out of App * use theme out * move theme context * move LanguageContext * move function into state * make sentry optional for now * add key to accordion * init langauge context on top * remove unneeded css files * move errorpage to tsx * add translation for error page * Add error to title * add typecast for error * move type definition out * remove todo -> type was already added * upgrade deps * add bootstrap * remove @mantine/core * readd core * switch to bootstrap * simplify import * Add SPA views for react #2789 * split up frontend urls * Add settings for frontend url loading * add new UI scaffold * remove tracking insert * add platform app * ensure static indexes work too * add lingui * add lingui config * add mgmt tasks * add base locales * settings for frontend dev * fix typo * update deps * add pre-commit * add eslint * add testing scaffold * fix paths * remove error - tests trip correctly * merge workflow * cleanup samples * use name inline with other tests * Add real worl frontend tests * setup env * tun migrations first * optimize setup time * setup demo dataset * optimize run setup * add test for class ui * rename * fix typo * and another typo * do install * run migrations first * fix name * cleanup * use other credentials * use other credentials * fix qc * move envs to qc * remove create_site * reduce testing env * fix test * fix test call * allaccess user * add ui plattform check * add better check * remove unneeded env * enable debug * reduce wait time * also build frontend on static * add sekeleton * fix various issues * add locales * clean output before building * cleanup dir * remove bootstrap * clean up deps * fix settings panel * remove assets * move logo * split out router * split up chunks * fix zustand import syntax * bundl * update pre-render * use vendor splitting * maximes space usage * enlarge breakpoints * remove wired color changes * cleanup tabs * fix error * update auth functions * default to mail login * add placeholder marking * Add text to placeholder * readd codespell * add another test * add sort plugin * add sort plugin * sort imports * fix order * Add mega menu * run pre-commit fixes * add node min version * Docker container (#129) * Fix allocation check for completing build order (#5199) - Allocation check only applies to untracked line items * docker dev Install required node packages to docker development image * add import order settings * cleanup settings * cleanup dashboard * clanup part tab * refactor header to only use 1 line * cleanup reqs for py3.9 * remove compiled UI * revert reqs change * cleanup tasks * cleanout built ui * remove default user * cleanup package.json * fix doctip * remove sentry * optimize loading * reset versions * clean * factor out menu items * refactor Navtabs * refactor HoverMenu * remove part * remove prettier * remove default arg from build * remove eslint * Merge branch 'plattform' of https://github.com/matmair/InvenTree into platform-ui_base * optimize svg * add build step for plattform UI * fix install command * fix test * remove extra test * set default host if none is set * set nicer names * fix tests * fix logged-in test * update translations * ensure more path matches * make loading of serverrefs dynamic * use default radius * fix issues / code smells * clean code smell * fix password reset * fix error messages * detect small screens * use loader meachanism for views * refactor structure * move auth functions out * use text * refactor defaults * Add email login FR] Add email link based logins Closes #3531 * fix name error * fix reqs * fix backend for magic login * fix frontend * remove unused route * remove now unneeded test section * cleanup code * add navigation header * fix logo component for nesting * factor out menu * refactor style * clean code * Translate items and use unique ids * use alpine commands * increase margins to remove drawer scroller * only render plugins if they are defined * remove sample content * fix assertation * open on hover * refactor * merge fix * cleanup navigation drawer * change dependencies for UI testing * add highlight filter * Add correct menu items * move design component out * move pre-commit out * move deps again * move js styles in * revert CI changes * remove unneeded exclution * changed placeholder user * refactored EditButton to component * refactored app loading to useEffect * moved color lookup into global scope * reafactored UserPanel render block * marked placeholder pages in doc links * made doc tooltip optional for MenuLink * changed MenuLinkItem names * fixes missing Link item in MenuLink * fixed merge error --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
parent
9f96620e12
commit
1ab772e66d
@ -52,7 +52,8 @@ repos:
|
||||
(?x)^(
|
||||
docs/docs/stylesheets/.*|
|
||||
docs/docs/javascripts/.*|
|
||||
docs/docs/webfonts/.*
|
||||
docs/docs/webfonts/.* |
|
||||
src/frontend/src/locales/.* |
|
||||
)$
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v3.0.0-alpha.9-for-vscode"
|
||||
|
@ -33,6 +33,7 @@ from report.api import report_api_urls
|
||||
from stock.api import stock_api_urls
|
||||
from stock.urls import stock_urls
|
||||
from users.api import user_urls
|
||||
from web.urls import spa_view
|
||||
from web.urls import urlpatterns as platform_urls
|
||||
|
||||
from .api import APISearchView, InfoView, NotFoundView
|
||||
@ -210,7 +211,7 @@ classic_frontendpatterns = [
|
||||
new_frontendpatterns = [
|
||||
# Platform urls
|
||||
re_path(r'^platform/', include(platform_urls)),
|
||||
|
||||
re_path(r'^platform', spa_view, name='platform'),
|
||||
]
|
||||
|
||||
# Load patterns for frontend according to settings
|
||||
|
@ -22,5 +22,6 @@ spa_view = ensure_csrf_cookie(TemplateView.as_view(template_name="web/index.html
|
||||
urlpatterns = [
|
||||
path("assets/<path:path>", RedirectAssetView.as_view()),
|
||||
re_path(r"^(?P<path>.*)/$", spa_view),
|
||||
path("set-password?uid=<uid>&token=<token>", spa_view, name="password_reset_confirm"),
|
||||
path("", spa_view),
|
||||
]
|
||||
|
@ -12,8 +12,5 @@
|
||||
"fallbackLocales": {
|
||||
"default": "en",
|
||||
"pseudo-LOCALE": "en"
|
||||
},
|
||||
"extractBabelOptions": {
|
||||
"presets": ["@babel/preset-typescript"]
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/inventree.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>InvenTree</title>
|
||||
</head>
|
||||
|
@ -11,10 +11,29 @@
|
||||
"compile": "lingui compile --typescript"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@lingui/core": "^4.2.1",
|
||||
"@lingui/react": "^4.2.1",
|
||||
"@mantine/core": "^6.0.15",
|
||||
"@mantine/dates": "^6.0.15",
|
||||
"@mantine/dropzone": "^6.0.15",
|
||||
"@mantine/form": "^6.0.15",
|
||||
"@mantine/hooks": "^6.0.15",
|
||||
"@mantine/modals": "^6.0.15",
|
||||
"@mantine/notifications": "^6.0.15",
|
||||
"@tabler/icons-react": "^2.23.0",
|
||||
"@tanstack/react-query": "^4.16.1",
|
||||
"axios": "^1.1.3",
|
||||
"dayjs": "^1.11.6",
|
||||
"html5-qrcode": "^2.3.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.4.3",
|
||||
"zustand": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.5",
|
||||
|
@ -1,10 +1,38 @@
|
||||
import { useViewportSize } from '@mantine/hooks';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
import { lazy } from 'react';
|
||||
|
||||
import { Loadable } from './functions/loading';
|
||||
import { useLocalState } from './states/LocalState';
|
||||
import { useSessionState } from './states/SessionState';
|
||||
|
||||
// API
|
||||
export const api = axios.create({});
|
||||
export function setApiDefaults() {
|
||||
const host = useLocalState.getState().host;
|
||||
const token = useSessionState.getState().token;
|
||||
|
||||
api.defaults.baseURL = host;
|
||||
api.defaults.headers.common['Authorization'] = `Token ${token}`;
|
||||
}
|
||||
export const queryClient = new QueryClient();
|
||||
|
||||
function checkMobile() {
|
||||
const { height, width } = useViewportSize();
|
||||
if (width < 425 || height < 425) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Main App
|
||||
export default function App() {
|
||||
// Check if mobile
|
||||
if (checkMobile()) {
|
||||
const MobileAppView = Loadable(lazy(() => import('./views/MobileAppView')));
|
||||
return <MobileAppView />;
|
||||
}
|
||||
|
||||
// Main App component
|
||||
return (
|
||||
<>
|
||||
<h1>Welcome to the new frontend!</h1>
|
||||
<p>This is a placeholder site</p>
|
||||
</>
|
||||
);
|
||||
const DesktopAppView = Loadable(lazy(() => import('./views/DesktopAppView')));
|
||||
return <DesktopAppView />;
|
||||
}
|
||||
|
154
src/frontend/src/components/forms/AuthenticationForm.tsx
Normal file
154
src/frontend/src/components/forms/AuthenticationForm.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
Anchor,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
PasswordInput,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconCheck } from '@tabler/icons-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { doClassicLogin, doSimpleLogin } from '../../functions/auth';
|
||||
import { EditButton } from '../items/EditButton';
|
||||
|
||||
export function AuthenticationForm({
|
||||
hostname,
|
||||
editing,
|
||||
setEditing,
|
||||
selectElement
|
||||
}: {
|
||||
hostname: string;
|
||||
editing: boolean;
|
||||
setEditing: (value?: React.SetStateAction<boolean> | undefined) => void;
|
||||
selectElement: JSX.Element;
|
||||
}) {
|
||||
const classicForm = useForm({
|
||||
initialValues: { username: '', password: '' }
|
||||
});
|
||||
const simpleForm = useForm({ initialValues: { email: '' } });
|
||||
const [classicLoginMode, setMode] = useDisclosure(true);
|
||||
const navigate = useNavigate();
|
||||
|
||||
function handleLogin() {
|
||||
if (classicLoginMode === true) {
|
||||
doClassicLogin(
|
||||
classicForm.values.username,
|
||||
classicForm.values.password
|
||||
).then((ret) => {
|
||||
if (ret === false) {
|
||||
notifications.show({
|
||||
title: t`Login failed`,
|
||||
message: t`Check your your input and try again.`,
|
||||
color: 'red'
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
title: t`Login successfull`,
|
||||
message: t`Welcome back!`,
|
||||
color: 'green',
|
||||
icon: <IconCheck size="1rem" />
|
||||
});
|
||||
navigate('/home');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doSimpleLogin(simpleForm.values.email).then((ret) => {
|
||||
if (ret?.status === 'ok') {
|
||||
notifications.show({
|
||||
title: t`Mail delivery successfull`,
|
||||
message: t`Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too.`,
|
||||
color: 'green',
|
||||
icon: <IconCheck size="1rem" />,
|
||||
autoClose: false
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
title: t`Input error`,
|
||||
message: t`Check your your input and try again.`,
|
||||
color: 'red'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper radius="md" p="xl" withBorder>
|
||||
<Text size="lg" weight={500}>
|
||||
<Group>
|
||||
{!editing ? hostname : selectElement}
|
||||
<EditButton setEditing={setEditing} editing={editing} />
|
||||
</Group>
|
||||
</Text>
|
||||
<form onSubmit={classicForm.onSubmit(() => {})}>
|
||||
{classicLoginMode ? (
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Username`}
|
||||
placeholder="reader"
|
||||
{...classicForm.getInputProps('username')}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password`}
|
||||
placeholder={t`Your password`}
|
||||
{...classicForm.getInputProps('password')}
|
||||
/>
|
||||
<Group position="apart" mt="0">
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => navigate('/reset-password')}
|
||||
>
|
||||
<Trans>Reset password</Trans>
|
||||
</Anchor>
|
||||
</Group>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Email`}
|
||||
description={t`We will send you a link to login - if you are registered`}
|
||||
placeholder="reader@example.org"
|
||||
{...simpleForm.getInputProps('email')}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Group position="apart" mt="xl">
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => setMode.toggle()}
|
||||
>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Send me an email</Trans>
|
||||
) : (
|
||||
<Trans>I will use username and password</Trans>
|
||||
)}
|
||||
</Anchor>
|
||||
<Button type="submit" onClick={handleLogin}>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Log in</Trans>
|
||||
) : (
|
||||
<Trans>Send mail</Trans>
|
||||
)}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Paper>
|
||||
);
|
||||
}
|
95
src/frontend/src/components/forms/HostOptionsForm.tsx
Normal file
95
src/frontend/src/components/forms/HostOptionsForm.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Space,
|
||||
Text,
|
||||
TextInput
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { randomId } from '@mantine/hooks';
|
||||
import { IconSquarePlus, IconTrash } from '@tabler/icons-react';
|
||||
|
||||
import { HostList } from '../../states/states';
|
||||
|
||||
export function HostOptionsForm({
|
||||
data,
|
||||
saveOptions
|
||||
}: {
|
||||
data: HostList;
|
||||
saveOptions: (newData: HostList) => void;
|
||||
}) {
|
||||
const form = useForm({ initialValues: data });
|
||||
function deleteItem(key: string) {
|
||||
const newData = form.values;
|
||||
delete newData[key];
|
||||
form.setValues(newData);
|
||||
}
|
||||
|
||||
const fields = Object.entries(form.values).map(([key]) => (
|
||||
<Group key={key} mt="xs">
|
||||
{form.values[key] !== undefined && (
|
||||
<>
|
||||
<TextInput
|
||||
placeholder={t`Host`}
|
||||
withAsterisk
|
||||
sx={{ flex: 1 }}
|
||||
{...form.getInputProps(`${key}.host`)}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={t`Name`}
|
||||
withAsterisk
|
||||
sx={{ flex: 1 }}
|
||||
{...form.getInputProps(`${key}.name`)}
|
||||
/>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
onClick={() => {
|
||||
deleteItem(key);
|
||||
}}
|
||||
>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
));
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(saveOptions)}>
|
||||
<Box sx={{ maxWidth: 500 }} mx="auto">
|
||||
{fields.length > 0 ? (
|
||||
<Group mb="xs">
|
||||
<Text weight={500} size="sm" sx={{ flex: 1 }}>
|
||||
<Trans>Host</Trans>
|
||||
</Text>
|
||||
<Text weight={500} size="sm" sx={{ flex: 1 }}>
|
||||
<Trans>Name</Trans>
|
||||
</Text>
|
||||
</Group>
|
||||
) : (
|
||||
<Text color="dimmed" align="center">
|
||||
<Trans>No one here...</Trans>
|
||||
</Text>
|
||||
)}
|
||||
{fields}
|
||||
<Group mt="md">
|
||||
<Button
|
||||
onClick={() =>
|
||||
form.setFieldValue(`${randomId()}`, { name: '', host: '' })
|
||||
}
|
||||
>
|
||||
<IconSquarePlus />
|
||||
<Trans>Add Host</Trans>
|
||||
</Button>
|
||||
<Space sx={{ flex: 1 }} />
|
||||
<Button type="submit">
|
||||
<Trans>Save</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
</Box>
|
||||
</form>
|
||||
);
|
||||
}
|
27
src/frontend/src/components/items/ColorToggle.tsx
Normal file
27
src/frontend/src/components/items/ColorToggle.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { ActionIcon, Group, useMantineColorScheme } from '@mantine/core';
|
||||
import { IconMoonStars, IconSun } from '@tabler/icons-react';
|
||||
|
||||
export function ColorToggle() {
|
||||
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||
|
||||
return (
|
||||
<Group position="center">
|
||||
<ActionIcon
|
||||
onClick={() => toggleColorScheme()}
|
||||
size="lg"
|
||||
sx={(theme) => ({
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[6]
|
||||
: theme.colors.gray[0],
|
||||
color:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.yellow[4]
|
||||
: theme.colors.blue[6]
|
||||
})}
|
||||
>
|
||||
{colorScheme === 'dark' ? <IconSun /> : <IconMoonStars />}
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
);
|
||||
}
|
95
src/frontend/src/components/items/DocTooltip.tsx
Normal file
95
src/frontend/src/components/items/DocTooltip.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Anchor, Container, HoverCard, ScrollArea, Text } from '@mantine/core';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
|
||||
export function DocTooltip({
|
||||
children,
|
||||
text,
|
||||
detail,
|
||||
link,
|
||||
docchildren
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
text: string | JSX.Element;
|
||||
detail?: string | JSX.Element;
|
||||
link?: string;
|
||||
docchildren?: React.ReactNode;
|
||||
}) {
|
||||
const { classes } = InvenTreeStyle();
|
||||
|
||||
return (
|
||||
<HoverCard
|
||||
shadow="md"
|
||||
openDelay={200}
|
||||
closeDelay={200}
|
||||
withinPortal={true}
|
||||
classNames={{ dropdown: classes.docHover }}
|
||||
>
|
||||
<HoverCard.Target>
|
||||
<div>{children}</div>
|
||||
</HoverCard.Target>
|
||||
<HoverCard.Dropdown>
|
||||
<ConstBody
|
||||
text={text}
|
||||
detail={detail}
|
||||
docchildren={docchildren}
|
||||
link={link}
|
||||
/>
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
||||
function ConstBody({
|
||||
text,
|
||||
detail,
|
||||
docchildren,
|
||||
link
|
||||
}: {
|
||||
text: string | JSX.Element;
|
||||
detail?: string | JSX.Element;
|
||||
docchildren?: React.ReactNode;
|
||||
link?: string;
|
||||
}) {
|
||||
const [height, setHeight] = useState(0);
|
||||
const ref = useRef(null);
|
||||
|
||||
// dynamically set height of scroll area based on content to remove unnecessary scroll bar
|
||||
useEffect(() => {
|
||||
if (ref.current == null) return;
|
||||
|
||||
let height = ref.current['clientHeight'];
|
||||
if (height > 250) {
|
||||
setHeight(250);
|
||||
} else {
|
||||
setHeight(height + 1);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Container maw={400} p={0}>
|
||||
<Text>{text}</Text>
|
||||
{(detail || docchildren) && (
|
||||
<ScrollArea h={height} mah={250}>
|
||||
<div ref={ref}>
|
||||
{detail && (
|
||||
<Text size="xs" color="dimmed">
|
||||
{detail}
|
||||
</Text>
|
||||
)}
|
||||
{docchildren}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)}
|
||||
{link && (
|
||||
<Anchor href={link} target="_blank">
|
||||
<Text size={'sm'}>
|
||||
<Trans>Read More</Trans>
|
||||
</Text>
|
||||
</Anchor>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
41
src/frontend/src/components/items/DocumentationLinks.tsx
Normal file
41
src/frontend/src/components/items/DocumentationLinks.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { Anchor, Group, SimpleGrid, Text } from '@mantine/core';
|
||||
|
||||
import { DocTooltip } from './DocTooltip';
|
||||
import { PlaceholderPill } from './Placeholder';
|
||||
|
||||
export interface DocumentationLinkItem {
|
||||
id: string;
|
||||
title: string | JSX.Element;
|
||||
description: string | JSX.Element;
|
||||
link: string;
|
||||
placeholder?: boolean;
|
||||
}
|
||||
|
||||
export function DocumentationLinks({
|
||||
links
|
||||
}: {
|
||||
links: DocumentationLinkItem[];
|
||||
}) {
|
||||
return (
|
||||
<SimpleGrid cols={2} spacing={0}>
|
||||
{links.map((link) => (
|
||||
<DocTooltip key={link.id} text={link.description}>
|
||||
<Anchor href={link.link} key={link.id}>
|
||||
{link.placeholder ? (
|
||||
<Group>
|
||||
<Text size="sm" fw={500}>
|
||||
{link.title}
|
||||
</Text>
|
||||
<PlaceholderPill />
|
||||
</Group>
|
||||
) : (
|
||||
<Text size="sm" fw={500}>
|
||||
{link.title}
|
||||
</Text>
|
||||
)}
|
||||
</Anchor>
|
||||
</DocTooltip>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
);
|
||||
}
|
18
src/frontend/src/components/items/EditButton.tsx
Normal file
18
src/frontend/src/components/items/EditButton.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { ActionIcon } from '@mantine/core';
|
||||
import { IconDeviceFloppy, IconEdit } from '@tabler/icons-react';
|
||||
|
||||
export function EditButton({
|
||||
setEditing,
|
||||
editing,
|
||||
disabled
|
||||
}: {
|
||||
setEditing: (value?: React.SetStateAction<boolean> | undefined) => void;
|
||||
editing: boolean;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<ActionIcon onClick={() => setEditing()} disabled={disabled}>
|
||||
{editing ? <IconDeviceFloppy /> : <IconEdit />}
|
||||
</ActionIcon>
|
||||
);
|
||||
}
|
15
src/frontend/src/components/items/ErrorItem.tsx
Normal file
15
src/frontend/src/components/items/ErrorItem.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
export function ErrorItem({ id, error }: { id: string; error?: any }) {
|
||||
const error_message = error?.message || error?.toString() || (
|
||||
<Trans>Unknown error</Trans>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Trans>An error occurred:</Trans>
|
||||
</p>
|
||||
{error_message}
|
||||
</>
|
||||
);
|
||||
}
|
18
src/frontend/src/components/items/InvenTreeLogo.tsx
Normal file
18
src/frontend/src/components/items/InvenTreeLogo.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { ActionIcon } from '@mantine/core';
|
||||
import { forwardRef } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import InvenTreeIcon from './inventree.svg';
|
||||
|
||||
export const InvenTreeLogo = forwardRef<HTMLDivElement>((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} {...props}>
|
||||
<NavLink to={'/'}>
|
||||
<ActionIcon size={28}>
|
||||
<img src={InvenTreeIcon} alt={t`InvenTree Logo`} height={28} />
|
||||
</ActionIcon>
|
||||
</NavLink>
|
||||
</div>
|
||||
);
|
||||
});
|
73
src/frontend/src/components/items/MenuLinks.tsx
Normal file
73
src/frontend/src/components/items/MenuLinks.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { Anchor, SimpleGrid, Text, UnstyledButton } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { DocTooltip } from './DocTooltip';
|
||||
|
||||
export interface MenuLinkItem {
|
||||
id: string;
|
||||
text: string | JSX.Element;
|
||||
link: string;
|
||||
highlight?: boolean;
|
||||
doctext?: string | JSX.Element;
|
||||
docdetail?: string | JSX.Element;
|
||||
doclink?: string;
|
||||
docchildren?: React.ReactNode;
|
||||
}
|
||||
|
||||
function ConditionalDocTooltip({
|
||||
item,
|
||||
children
|
||||
}: {
|
||||
item: MenuLinkItem;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
if (item.doctext !== undefined) {
|
||||
return (
|
||||
<DocTooltip
|
||||
key={item.id}
|
||||
text={item.doctext}
|
||||
detail={item?.docdetail}
|
||||
link={item?.doclink}
|
||||
docchildren={item?.docchildren}
|
||||
>
|
||||
{children}
|
||||
</DocTooltip>
|
||||
);
|
||||
}
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
export function MenuLinks({
|
||||
links,
|
||||
highlighted
|
||||
}: {
|
||||
links: MenuLinkItem[];
|
||||
highlighted?: boolean;
|
||||
}) {
|
||||
const { classes } = InvenTreeStyle();
|
||||
highlighted = highlighted || false;
|
||||
|
||||
const filteredLinks = links.filter(
|
||||
(item) => !highlighted || item.highlight === true
|
||||
);
|
||||
return (
|
||||
<SimpleGrid cols={2} spacing={0}>
|
||||
{filteredLinks.map((item) => (
|
||||
<ConditionalDocTooltip item={item} key={item.id}>
|
||||
<UnstyledButton
|
||||
className={classes.subLink}
|
||||
component={Link}
|
||||
to={item.link}
|
||||
p={0}
|
||||
>
|
||||
<Text size="sm" fw={500}>
|
||||
{item.text}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</ConditionalDocTooltip>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
);
|
||||
}
|
17
src/frontend/src/components/items/Placeholder.tsx
Normal file
17
src/frontend/src/components/items/Placeholder.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Badge, Tooltip } from '@mantine/core';
|
||||
|
||||
export function PlaceholderPill() {
|
||||
return (
|
||||
<Tooltip
|
||||
multiline
|
||||
width={220}
|
||||
withArrow
|
||||
label={t`This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing.`}
|
||||
>
|
||||
<Badge color="teal" variant="outline">
|
||||
<Trans>PLH</Trans>
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
20
src/frontend/src/components/items/ScanButton.tsx
Normal file
20
src/frontend/src/components/items/ScanButton.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { ActionIcon } from '@mantine/core';
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
import { IconQrcode } from '@tabler/icons-react';
|
||||
|
||||
export function ScanButton() {
|
||||
return (
|
||||
<ActionIcon
|
||||
onClick={() =>
|
||||
openContextModal({
|
||||
modal: 'qr',
|
||||
title: t`Scan QR code`,
|
||||
innerProps: {}
|
||||
})
|
||||
}
|
||||
>
|
||||
<IconQrcode />
|
||||
</ActionIcon>
|
||||
);
|
||||
}
|
12
src/frontend/src/components/items/StylishText.tsx
Normal file
12
src/frontend/src/components/items/StylishText.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Text } from '@mantine/core';
|
||||
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
|
||||
export function StylishText({ children }: { children: JSX.Element | string }) {
|
||||
const { classes } = InvenTreeStyle();
|
||||
return (
|
||||
<Text className={classes.signText} variant="gradient">
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
}
|
1
src/frontend/src/components/items/inventree.svg
Normal file
1
src/frontend/src/components/items/inventree.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400"><g stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.676"><path fill="#ddbc91" d="M200 199.906v193.196l166.305-96.016V103.139Z"/><path fill="#d9b383" d="M200 199.906v193.196L33.695 297.086V103.139Z"/><path fill="#ddbc91" d="M366.305 103.139 200 199.906 33.695 103.14 200 6.899z"/><g fill="#f3f4e4"><path d="M75.27 127.109v145.46L200 344.583l124.73-72.012V127.109L200 199.684Z"/><path d="M324.73 127.11 200 199.684 75.27 127.109 200 54.93Z"/></g><path fill="#d9dbbc" d="M200 54.929v144.756L75.27 272.569V127.11z"/><path fill="#eaeccf" d="M200 54.929v144.756l124.73 72.884V127.11z"/><path fill="#90a8d8" d="M200 199.901V296.5l83.153-48.008v-96.973zm0 0V296.5l-83.153-48.008v-96.973zm83.153-48.383L200 199.9l-83.153-48.383L200 103.398Z"/></g><g fill="#90a8d8" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"><path d="M-546.667 443.959v216.54l186.4-107.618V335.499Zm0 0v216.54l-186.4-107.618V335.499Z"/><path d="m-360.266 335.499-186.4 108.46-186.401-108.46 186.4-107.87z"/></g><g fill="#90a8d8" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"><path d="M-546.667 227.419v216.54l186.4-107.619V118.96Zm0 0v216.54l-186.4-107.619V118.96Z"/><path d="m-360.266 118.959-186.4 108.46-186.401-108.46 186.4-107.87z"/></g><path fill="#ddbc91" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M-546.667 10.879v216.54l186.4-107.619V-97.58Z"/><path fill="#d9b383" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.676" d="M-546.667 10.879v216.54l-186.4-107.619V-97.58Z"/><path fill="#ddbc91" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="m-360.266-97.581-186.4 108.46-186.401-108.46 186.4-107.87z"/><g fill="#f3f4e4" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.058"><path stroke-width="3" d="M-686.466-70.715V92.321l139.798 80.714 139.801-80.714V-70.715l-139.8 81.346z"/><path stroke-width="2.999" d="m-406.866-70.715-139.8 81.345-139.801-81.345 139.8-80.902Z"/></g><path fill="#d9dbbc" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M-546.667-151.617V10.63l-139.8 81.692V-70.715z"/><path fill="#eaeccf" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M-546.667-151.617V10.63l139.8 81.692V-70.715z"/><g fill="#90a8d8" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.999"><path d="M-546.667 10.873v108.27l93.2-53.81v-108.69zm0 0v108.27l-93.2-53.81v-108.69z"/><path d="m-453.466-43.357-93.2 54.23-93.201-54.23 93.2-53.935Z"/></g><path fill="none" d="M-787.948-253.366h482.56v946.733h-482.56z"/></svg>
|
After Width: | Height: | Size: 2.7 KiB |
201
src/frontend/src/components/modals/QrCodeModal.tsx
Normal file
201
src/frontend/src/components/modals/QrCodeModal.tsx
Normal file
@ -0,0 +1,201 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Container,
|
||||
Group,
|
||||
ScrollArea,
|
||||
Space,
|
||||
Stack,
|
||||
Text
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
useDocumentVisibility,
|
||||
useListState,
|
||||
useLocalStorage
|
||||
} from '@mantine/hooks';
|
||||
import { ContextModalProps } from '@mantine/modals';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconX } from '@tabler/icons-react';
|
||||
import { Html5Qrcode } from 'html5-qrcode';
|
||||
import { CameraDevice } from 'html5-qrcode/camera/core';
|
||||
import { Html5QrcodeResult } from 'html5-qrcode/core';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { api } from '../../App';
|
||||
|
||||
export function QrCodeModal({
|
||||
context,
|
||||
id
|
||||
}: ContextModalProps<{ modalBody: string }>) {
|
||||
const [qrCodeScanner, setQrCodeScanner] = useState<Html5Qrcode | null>(null);
|
||||
const [camId, setCamId] = useLocalStorage<CameraDevice | null>({
|
||||
key: 'camId',
|
||||
defaultValue: null
|
||||
});
|
||||
const [ScanningEnabled, setIsScanning] = useState<boolean>(false);
|
||||
const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false);
|
||||
const documentState = useDocumentVisibility();
|
||||
|
||||
const [values, handlers] = useListState<string>([]);
|
||||
|
||||
// Mount QR code once we are loaded
|
||||
useEffect(() => {
|
||||
setQrCodeScanner(new Html5Qrcode('reader'));
|
||||
}, []);
|
||||
|
||||
// Stop/star when leaving or reentering page
|
||||
useEffect(() => {
|
||||
if (ScanningEnabled && documentState === 'hidden') {
|
||||
stopScanning();
|
||||
setWasAutoPaused(true);
|
||||
} else if (wasAutoPaused && documentState === 'visible') {
|
||||
startScanning();
|
||||
setWasAutoPaused(false);
|
||||
}
|
||||
}, [documentState]);
|
||||
|
||||
// Scanner functions
|
||||
function onScanSuccess(
|
||||
decodedText: string,
|
||||
decodedResult: Html5QrcodeResult
|
||||
) {
|
||||
qrCodeScanner?.pause();
|
||||
|
||||
handlers.append(decodedText);
|
||||
api.post('/barcode/', { barcode: decodedText }).then((response) => {
|
||||
showNotification({
|
||||
title: response.data?.success || t`Unknown response`,
|
||||
message: JSON.stringify(response.data),
|
||||
color: response.data?.success ? 'teal' : 'red'
|
||||
});
|
||||
if (response.data?.url) {
|
||||
window.location.href = response.data.url;
|
||||
}
|
||||
});
|
||||
|
||||
qrCodeScanner?.resume();
|
||||
}
|
||||
|
||||
function onScanFailure(error: string) {
|
||||
if (
|
||||
error !=
|
||||
'QR code parse error, error = NotFoundException: No MultiFormat Readers were able to detect the code.'
|
||||
) {
|
||||
console.warn(`Code scan error = ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
function selectCamera() {
|
||||
Html5Qrcode.getCameras()
|
||||
.then((devices) => {
|
||||
if (devices?.length) {
|
||||
setCamId(devices[0]);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
showNotification({
|
||||
title: t`Error while getting camera`,
|
||||
message: err,
|
||||
color: 'red',
|
||||
icon: <IconX />
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function startScanning() {
|
||||
if (camId && qrCodeScanner) {
|
||||
qrCodeScanner
|
||||
.start(
|
||||
camId.id,
|
||||
{ fps: 10, qrbox: { width: 250, height: 250 } },
|
||||
(decodedText, decodedResult) => {
|
||||
onScanSuccess(decodedText, decodedResult);
|
||||
},
|
||||
(errorMessage) => {
|
||||
onScanFailure(errorMessage);
|
||||
}
|
||||
)
|
||||
.catch((err: string) => {
|
||||
showNotification({
|
||||
title: t`Error while scanning`,
|
||||
message: err,
|
||||
color: 'red',
|
||||
icon: <IconX />
|
||||
});
|
||||
});
|
||||
setIsScanning(true);
|
||||
}
|
||||
}
|
||||
|
||||
function stopScanning() {
|
||||
if (qrCodeScanner && ScanningEnabled) {
|
||||
qrCodeScanner.stop().catch((err: string) => {
|
||||
showNotification({
|
||||
title: t`Error while stopping`,
|
||||
message: err,
|
||||
color: 'red',
|
||||
icon: <IconX />
|
||||
});
|
||||
});
|
||||
setIsScanning(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group>
|
||||
<Text size="sm">{camId?.label}</Text>
|
||||
<Space sx={{ flex: 1 }} />
|
||||
<Badge>{ScanningEnabled ? t`Scanning` : t`Not scanning`}</Badge>
|
||||
</Group>
|
||||
<Container px={0} id="reader" w={'100%'} mih="300px" />
|
||||
{!camId ? (
|
||||
<Button onClick={() => selectCamera()}>
|
||||
<Trans>Select Camera</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Group>
|
||||
<Button
|
||||
sx={{ flex: 1 }}
|
||||
onClick={() => startScanning()}
|
||||
disabled={camId != undefined && ScanningEnabled}
|
||||
>
|
||||
<Trans>Start scanning</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
sx={{ flex: 1 }}
|
||||
onClick={() => stopScanning()}
|
||||
disabled={!ScanningEnabled}
|
||||
>
|
||||
<Trans>Stop scanning</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
{values.length == 0 ? (
|
||||
<Text color={'grey'}>
|
||||
<Trans>No scans yet!</Trans>
|
||||
</Text>
|
||||
) : (
|
||||
<ScrollArea sx={{ height: 200 }} type="auto" offsetScrollbars>
|
||||
{values.map((value, index) => (
|
||||
<div key={index}>{value}</div>
|
||||
))}
|
||||
</ScrollArea>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
fullWidth
|
||||
mt="md"
|
||||
color="red"
|
||||
onClick={() => {
|
||||
stopScanning();
|
||||
context.closeModal(id);
|
||||
}}
|
||||
>
|
||||
<Trans>Close modal</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
}
|
29
src/frontend/src/components/nav/Footer.tsx
Normal file
29
src/frontend/src/components/nav/Footer.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Anchor, Container, Group } from '@mantine/core';
|
||||
|
||||
import { footerLinks } from '../../defaults/links';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { InvenTreeLogo } from '../items/InvenTreeLogo';
|
||||
|
||||
export function Footer() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const items = footerLinks.map((link) => (
|
||||
<Anchor<'a'>
|
||||
color="dimmed"
|
||||
key={link.key}
|
||||
href={link.link}
|
||||
onClick={(event) => event.preventDefault()}
|
||||
size="sm"
|
||||
>
|
||||
{link.label}
|
||||
</Anchor>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={classes.layoutFooter}>
|
||||
<Container className={classes.layoutFooterInner} size={'xl'}>
|
||||
<InvenTreeLogo />
|
||||
<Group className={classes.layoutFooterLinks}>{items}</Group>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
64
src/frontend/src/components/nav/Header.tsx
Normal file
64
src/frontend/src/components/nav/Header.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import { Container, Group, Tabs } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { navTabs as mainNavTabs } from '../../defaults/links';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { ColorToggle } from '../items/ColorToggle';
|
||||
import { ScanButton } from '../items/ScanButton';
|
||||
import { MainMenu } from './MainMenu';
|
||||
import { NavHoverMenu } from './NavHoverMenu';
|
||||
import { NavigationDrawer } from './NavigationDrawer';
|
||||
|
||||
export function Header() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const [drawerOpened, { open: openDrawer, close }] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<div className={classes.layoutHeader}>
|
||||
<NavigationDrawer opened={drawerOpened} close={close} />
|
||||
<Container className={classes.layoutHeaderSection} size={'xl'}>
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
<NavHoverMenu openDrawer={openDrawer} />
|
||||
<NavTabs />
|
||||
</Group>
|
||||
<Group>
|
||||
<ScanButton />
|
||||
<ColorToggle />
|
||||
<MainMenu />
|
||||
</Group>
|
||||
</Group>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NavTabs() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const { tabValue } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
defaultValue="home"
|
||||
classNames={{
|
||||
root: classes.tabs,
|
||||
tabsList: classes.tabsList,
|
||||
tab: classes.tab
|
||||
}}
|
||||
value={tabValue}
|
||||
onTabChange={(value) =>
|
||||
value == '/' ? navigate('/') : navigate(`/${value}`)
|
||||
}
|
||||
>
|
||||
<Tabs.List>
|
||||
{mainNavTabs.map((tab) => (
|
||||
<Tabs.Tab value={tab.name} key={tab.name}>
|
||||
{tab.text}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
34
src/frontend/src/components/nav/Layout.tsx
Normal file
34
src/frontend/src/components/nav/Layout.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Container, Flex, Space } from '@mantine/core';
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { useSessionState } from '../../states/SessionState';
|
||||
import { Footer } from './Footer';
|
||||
import { Header } from './Header';
|
||||
|
||||
export const ProtectedRoute = ({ children }: { children: JSX.Element }) => {
|
||||
const [token] = useSessionState((state) => [state.token]);
|
||||
|
||||
if (!token) {
|
||||
return <Navigate to="/logged-in" replace />;
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
export default function LayoutComponent() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<Flex direction="column" mih="100vh">
|
||||
<Header />
|
||||
<Container className={classes.layoutContent} size={'xl'}>
|
||||
<Outlet />
|
||||
</Container>
|
||||
<Space h="xl" />
|
||||
<Footer />
|
||||
</Flex>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
}
|
88
src/frontend/src/components/nav/MainMenu.tsx
Normal file
88
src/frontend/src/components/nav/MainMenu.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Group, Menu, Skeleton, Text, UnstyledButton } from '@mantine/core';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconHeart,
|
||||
IconLanguage,
|
||||
IconLogout,
|
||||
IconSettings,
|
||||
IconUserCircle
|
||||
} from '@tabler/icons-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { languages } from '../../contexts/LanguageContext';
|
||||
import { doClassicLogout } from '../../functions/auth';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { useApiState } from '../../states/ApiState';
|
||||
import { useLocalState } from '../../states/LocalState';
|
||||
import { PlaceholderPill } from '../items/Placeholder';
|
||||
|
||||
export function MainMenu() {
|
||||
const { classes, theme } = InvenTreeStyle();
|
||||
const [username] = useApiState((state) => [state.user?.name]);
|
||||
const [locale] = useLocalState((state) => [state.language]);
|
||||
|
||||
// Language
|
||||
function switchLanguage() {
|
||||
useLocalState.setState({
|
||||
language: languages[(languages.indexOf(locale) + 1) % languages.length]
|
||||
});
|
||||
}
|
||||
function enablePsuedo() {
|
||||
useLocalState.setState({ language: 'pseudo-LOCALE' });
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu width={260} position="bottom-end">
|
||||
<Menu.Target>
|
||||
<UnstyledButton className={classes.layoutHeaderUser}>
|
||||
<Group spacing={7}>
|
||||
<Text weight={500} size="sm" sx={{ lineHeight: 1 }} mr={3}>
|
||||
{username ? (
|
||||
username
|
||||
) : (
|
||||
<Skeleton height={20} width={40} radius={theme.defaultRadius} />
|
||||
)}
|
||||
</Text>
|
||||
<IconChevronDown />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item icon={<IconHeart />}>
|
||||
<Trans>Notifications</Trans>
|
||||
<PlaceholderPill />
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<IconUserCircle />}
|
||||
component={Link}
|
||||
to="/profile/user"
|
||||
>
|
||||
<Trans>Profile</Trans>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Label>
|
||||
<Trans>Settings</Trans>
|
||||
</Menu.Label>
|
||||
<Menu.Item icon={<IconLanguage />} onClick={switchLanguage}>
|
||||
<Trans>Current language {locale}</Trans>
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconLanguage />} onClick={enablePsuedo}>
|
||||
<Trans>Switch to pseudo language</Trans>
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconSettings />}>
|
||||
<Trans>Account settings</Trans>
|
||||
<PlaceholderPill />
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<IconLogout />}
|
||||
onClick={() => {
|
||||
doClassicLogout();
|
||||
}}
|
||||
>
|
||||
<Trans>Logout</Trans>
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
}
|
101
src/frontend/src/components/nav/NavHoverMenu.tsx
Normal file
101
src/frontend/src/components/nav/NavHoverMenu.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
ActionIcon,
|
||||
Anchor,
|
||||
Button,
|
||||
Divider,
|
||||
Group,
|
||||
HoverCard,
|
||||
Skeleton,
|
||||
Text
|
||||
} from '@mantine/core';
|
||||
import { IconLayoutSidebar } from '@tabler/icons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { menuItems } from '../../defaults/menuItems';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { useApiState } from '../../states/ApiState';
|
||||
import { useLocalState } from '../../states/LocalState';
|
||||
import { InvenTreeLogo } from '../items/InvenTreeLogo';
|
||||
import { MenuLinks } from '../items/MenuLinks';
|
||||
|
||||
export function NavHoverMenu({
|
||||
openDrawer: openDrawer
|
||||
}: {
|
||||
openDrawer: () => void;
|
||||
}) {
|
||||
const { classes, theme } = InvenTreeStyle();
|
||||
const [hostKey, hostList] = useLocalState((state) => [
|
||||
state.hostKey,
|
||||
state.hostList
|
||||
]);
|
||||
const [servername] = useApiState((state) => [state.server.instance]);
|
||||
const [instanceName, setInstanceName] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (hostKey && hostList[hostKey]) {
|
||||
setInstanceName(hostList[hostKey].name);
|
||||
}
|
||||
}, [hostKey]);
|
||||
|
||||
return (
|
||||
<HoverCard width={600} position="bottom" shadow="md" withinPortal>
|
||||
<HoverCard.Target>
|
||||
<InvenTreeLogo />
|
||||
</HoverCard.Target>
|
||||
|
||||
<HoverCard.Dropdown sx={{ overflow: 'hidden' }}>
|
||||
<Group position="apart" px="md">
|
||||
<ActionIcon
|
||||
onClick={openDrawer}
|
||||
onMouseOver={openDrawer}
|
||||
title={t`Open Navigation`}
|
||||
>
|
||||
<IconLayoutSidebar />
|
||||
</ActionIcon>
|
||||
<Group spacing={'xs'}>
|
||||
{instanceName ? (
|
||||
instanceName
|
||||
) : (
|
||||
<Skeleton height={20} width={40} radius={theme.defaultRadius} />
|
||||
)}{' '}
|
||||
|{' '}
|
||||
{servername ? (
|
||||
servername
|
||||
) : (
|
||||
<Skeleton height={20} width={40} radius={theme.defaultRadius} />
|
||||
)}
|
||||
</Group>
|
||||
<Anchor href="#" fz="xs" onClick={openDrawer}>
|
||||
<Trans>View all</Trans>
|
||||
</Anchor>
|
||||
</Group>
|
||||
|
||||
<Divider
|
||||
my="sm"
|
||||
mx="-md"
|
||||
color={theme.colorScheme === 'dark' ? 'dark.5' : 'gray.1'}
|
||||
/>
|
||||
<MenuLinks links={menuItems} highlighted={true} />
|
||||
<div className={classes.headerDropdownFooter}>
|
||||
<Group position="apart">
|
||||
<div>
|
||||
<Text fw={500} fz="sm">
|
||||
<Trans>Get started</Trans>
|
||||
</Text>
|
||||
<Text size="xs" color="dimmed">
|
||||
<Trans>
|
||||
Overview over high-level objects, functions and possible
|
||||
usecases.
|
||||
</Trans>
|
||||
</Text>
|
||||
</div>
|
||||
<Button variant="default">
|
||||
<Trans>Get started</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
</div>
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
85
src/frontend/src/components/nav/NavigationDrawer.tsx
Normal file
85
src/frontend/src/components/nav/NavigationDrawer.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Container,
|
||||
Drawer,
|
||||
Flex,
|
||||
ScrollArea,
|
||||
Space,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useViewportSize } from '@mantine/hooks';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { aboutLinks, navDocLinks } from '../../defaults/links';
|
||||
import { menuItems } from '../../defaults/menuItems';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { DocumentationLinks } from '../items/DocumentationLinks';
|
||||
import { MenuLinkItem, MenuLinks } from '../items/MenuLinks';
|
||||
|
||||
// TODO @matmair #1: implement plugin loading and menu item generation see #5269
|
||||
const plugins: MenuLinkItem[] = [];
|
||||
|
||||
export function NavigationDrawer({
|
||||
opened,
|
||||
close
|
||||
}: {
|
||||
opened: boolean;
|
||||
close: () => void;
|
||||
}) {
|
||||
const { classes } = InvenTreeStyle();
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
opened={opened}
|
||||
onClose={close}
|
||||
overlayProps={{ opacity: 0.5, blur: 4 }}
|
||||
withCloseButton={false}
|
||||
classNames={{
|
||||
body: classes.navigationDrawer
|
||||
}}
|
||||
>
|
||||
<DrawerContent />
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
function DrawerContent() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const [scrollHeight, setScrollHeight] = useState(0);
|
||||
const ref = useRef(null);
|
||||
const { height } = useViewportSize();
|
||||
|
||||
// update scroll height when viewport size changes
|
||||
useEffect(() => {
|
||||
if (ref.current == null) return;
|
||||
setScrollHeight(height - ref.current['clientHeight'] - 65);
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex direction="column" mih="100vh" p={16}>
|
||||
<Title order={3}>{t`Navigation`}</Title>
|
||||
<Container className={classes.layoutContent} p={0}>
|
||||
<ScrollArea h={scrollHeight} type="always" offsetScrollbars>
|
||||
<Title order={5}>{t`Pages`}</Title>
|
||||
<MenuLinks links={menuItems} />
|
||||
<Space h="md" />
|
||||
{plugins.length > 0 ? (
|
||||
<>
|
||||
<Title order={5}>{t`Plugins`}</Title>
|
||||
<MenuLinks links={plugins} />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</Container>
|
||||
<div ref={ref}>
|
||||
<Space h="md" />
|
||||
<Title order={5}>{t`Documentation`}</Title>
|
||||
<DocumentationLinks links={navDocLinks} />
|
||||
<Space h="md" />
|
||||
<Title order={5}>{t`About`}</Title>
|
||||
<DocumentationLinks links={aboutLinks} />
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
10
src/frontend/src/contexts/BaseContext.tsx
Normal file
10
src/frontend/src/contexts/BaseContext.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { LanguageContext } from './LanguageContext';
|
||||
import { ThemeContext } from './ThemeContext';
|
||||
|
||||
export const BaseContext = ({ children }: { children: any }) => {
|
||||
return (
|
||||
<LanguageContext>
|
||||
<ThemeContext>{children}</ThemeContext>
|
||||
</LanguageContext>
|
||||
);
|
||||
};
|
29
src/frontend/src/contexts/LanguageContext.tsx
Normal file
29
src/frontend/src/contexts/LanguageContext.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { i18n } from '@lingui/core';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { api } from '../App';
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
|
||||
// Definitions
|
||||
export type Locales = 'en' | 'de' | 'hu' | 'pseudo-LOCALE';
|
||||
export const languages: Locales[] = ['en', 'de', 'hu'];
|
||||
|
||||
export function LanguageContext({ children }: { children: JSX.Element }) {
|
||||
const [language] = useLocalState((state) => [state.language]);
|
||||
|
||||
useEffect(() => {
|
||||
activateLocale(language);
|
||||
}, [language]);
|
||||
|
||||
return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
|
||||
}
|
||||
|
||||
export async function activateLocale(locale: Locales) {
|
||||
const { messages } = await import(`../locales/${locale}/messages.ts`);
|
||||
i18n.load(locale, messages);
|
||||
i18n.activate(locale);
|
||||
|
||||
// Set api header
|
||||
api.defaults.headers.common['Accept-Language'] = locale;
|
||||
}
|
70
src/frontend/src/contexts/ThemeContext.tsx
Normal file
70
src/frontend/src/contexts/ThemeContext.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
ColorScheme,
|
||||
ColorSchemeProvider,
|
||||
MantineProvider,
|
||||
MantineThemeOverride
|
||||
} from '@mantine/core';
|
||||
import { useColorScheme, useLocalStorage } from '@mantine/hooks';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import { Notifications } from '@mantine/notifications';
|
||||
|
||||
import { QrCodeModal } from '../components/modals/QrCodeModal';
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
|
||||
export function ThemeContext({ children }: { children: JSX.Element }) {
|
||||
const [primaryColor, whiteColor, blackColor, radius, loader] = useLocalState(
|
||||
(state) => [
|
||||
state.primaryColor,
|
||||
state.whiteColor,
|
||||
state.blackColor,
|
||||
state.radius,
|
||||
state.loader
|
||||
]
|
||||
);
|
||||
|
||||
// Color Scheme
|
||||
const preferredColorScheme = useColorScheme();
|
||||
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
|
||||
key: 'scheme',
|
||||
defaultValue: preferredColorScheme
|
||||
});
|
||||
const toggleColorScheme = (value?: ColorScheme) => {
|
||||
setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));
|
||||
myTheme.colorScheme = colorScheme;
|
||||
};
|
||||
|
||||
// Theme
|
||||
const myTheme: MantineThemeOverride = {
|
||||
colorScheme: colorScheme,
|
||||
primaryColor: primaryColor,
|
||||
white: whiteColor,
|
||||
black: blackColor,
|
||||
loader: loader,
|
||||
defaultRadius: radius,
|
||||
breakpoints: {
|
||||
xs: '30em',
|
||||
sm: '48em',
|
||||
md: '64em',
|
||||
lg: '74em',
|
||||
xl: '90em'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ColorSchemeProvider
|
||||
colorScheme={colorScheme}
|
||||
toggleColorScheme={toggleColorScheme}
|
||||
>
|
||||
<MantineProvider theme={myTheme} withGlobalStyles withNormalizeCSS>
|
||||
<Notifications />
|
||||
<ModalsProvider
|
||||
labels={{ confirm: t`Submit`, cancel: t`Cancel` }}
|
||||
modals={{ qr: QrCodeModal }}
|
||||
>
|
||||
{children}
|
||||
</ModalsProvider>
|
||||
</MantineProvider>
|
||||
</ColorSchemeProvider>
|
||||
);
|
||||
}
|
19
src/frontend/src/defaults/defaultHostList.tsx
Normal file
19
src/frontend/src/defaults/defaultHostList.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
import { HostList } from '../states/states';
|
||||
|
||||
export const defaultHostList: HostList = {
|
||||
'mantine-u56l5jt85': {
|
||||
host: 'https://demo.inventree.org/api/',
|
||||
name: t`InvenTree Demo`
|
||||
},
|
||||
'mantine-g8t1zrj50': {
|
||||
host: 'https://sample.app.invenhost.com/api/',
|
||||
name: 'InvenHost: Sample'
|
||||
},
|
||||
'mantine-cqj63coxn': {
|
||||
host: 'http://localhost:8000/api/',
|
||||
name: t`Local Server`
|
||||
}
|
||||
};
|
||||
export const defaultHostKey = 'mantine-cqj63coxn';
|
25
src/frontend/src/defaults/defaults.tsx
Normal file
25
src/frontend/src/defaults/defaults.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { MantineSize } from '@mantine/core';
|
||||
|
||||
export const emptyServerAPI = {
|
||||
server: null,
|
||||
version: null,
|
||||
instance: null,
|
||||
apiVersion: null,
|
||||
worker_running: null,
|
||||
worker_pending_tasks: null,
|
||||
plugins_enabled: null,
|
||||
active_plugins: []
|
||||
};
|
||||
|
||||
export interface SiteMarkProps {
|
||||
value: number;
|
||||
label: MantineSize;
|
||||
}
|
||||
|
||||
export const SizeMarks: SiteMarkProps[] = [
|
||||
{ value: 0, label: 'xs' },
|
||||
{ value: 25, label: 'sm' },
|
||||
{ value: 50, label: 'md' },
|
||||
{ value: 75, label: 'lg' },
|
||||
{ value: 100, label: 'xl' }
|
||||
];
|
83
src/frontend/src/defaults/links.tsx
Normal file
83
src/frontend/src/defaults/links.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { DocumentationLinkItem } from '../components/items/DocumentationLinks';
|
||||
|
||||
export const footerLinks = [
|
||||
{
|
||||
link: 'https://inventree.org/',
|
||||
label: <Trans>Website</Trans>,
|
||||
key: 'website'
|
||||
},
|
||||
{
|
||||
link: 'https://github.com/invenhost/InvenTree',
|
||||
label: <Trans>GitHub</Trans>,
|
||||
key: 'github'
|
||||
},
|
||||
{
|
||||
link: 'https://demo.inventree.org/',
|
||||
label: <Trans>Demo</Trans>,
|
||||
key: 'demo'
|
||||
}
|
||||
];
|
||||
export const navTabs = [{ text: <Trans>Home</Trans>, name: 'home' }];
|
||||
|
||||
export const docLinks = {
|
||||
app: 'https://docs.inventree.org/en/latest/app/app/',
|
||||
getting_started: 'https://docs.inventree.org/en/latest/getting_started/',
|
||||
api: 'https://docs.inventree.org/en/latest/api/api/',
|
||||
developer: 'https://docs.inventree.org/en/latest/develop/starting/',
|
||||
faq: 'https://docs.inventree.org/en/latest/faq/'
|
||||
};
|
||||
|
||||
export const navDocLinks: DocumentationLinkItem[] = [
|
||||
{
|
||||
id: 'getting_started',
|
||||
title: <Trans>Getting Started</Trans>,
|
||||
description: <Trans>Getting started with InvenTree</Trans>,
|
||||
link: docLinks.getting_started,
|
||||
placeholder: true
|
||||
},
|
||||
{
|
||||
id: 'api',
|
||||
title: <Trans>API</Trans>,
|
||||
description: <Trans>InvenTree API documentation</Trans>,
|
||||
link: docLinks.api
|
||||
},
|
||||
{
|
||||
id: 'developer',
|
||||
title: <Trans>Developer Manual</Trans>,
|
||||
description: <Trans>InvenTree developer manual</Trans>,
|
||||
link: docLinks.developer
|
||||
},
|
||||
{
|
||||
id: 'faq',
|
||||
title: <Trans>FAQ</Trans>,
|
||||
description: <Trans>Frequently asked questions</Trans>,
|
||||
link: docLinks.faq
|
||||
}
|
||||
];
|
||||
|
||||
// TODO @matmair: Add the following pages and adjust the links
|
||||
export const aboutLinks: DocumentationLinkItem[] = [
|
||||
{
|
||||
id: 'instance',
|
||||
title: <Trans>Instance</Trans>,
|
||||
description: <Trans>About this Inventree instance</Trans>,
|
||||
link: '/instance',
|
||||
placeholder: true
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
title: <Trans>InvenTree</Trans>,
|
||||
description: <Trans>About the InvenTree org</Trans>,
|
||||
link: '/about',
|
||||
placeholder: true
|
||||
},
|
||||
{
|
||||
id: 'licenses',
|
||||
title: <Trans>Licenses</Trans>,
|
||||
description: <Trans>Licenses for packages used by InvenTree</Trans>,
|
||||
link: '/licenses',
|
||||
placeholder: true
|
||||
}
|
||||
];
|
19
src/frontend/src/defaults/menuItems.tsx
Normal file
19
src/frontend/src/defaults/menuItems.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Image, Text } from '@mantine/core';
|
||||
|
||||
import { MenuLinkItem } from '../components/items/MenuLinks';
|
||||
|
||||
export const menuItems: MenuLinkItem[] = [
|
||||
{
|
||||
id: 'home',
|
||||
text: <Trans>Home</Trans>,
|
||||
link: '/',
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
id: 'profile',
|
||||
text: <Trans>Profile page</Trans>,
|
||||
link: '/profile/user',
|
||||
doctext: <Trans>User attributes and design settings.</Trans>
|
||||
}
|
||||
];
|
112
src/frontend/src/functions/auth.tsx
Normal file
112
src/frontend/src/functions/auth.tsx
Normal file
@ -0,0 +1,112 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconCheck } from '@tabler/icons-react';
|
||||
import axios from 'axios';
|
||||
|
||||
import { api } from '../App';
|
||||
import { ApiPaths, url, useApiState } from '../states/ApiState';
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
import { useSessionState } from '../states/SessionState';
|
||||
|
||||
export const doClassicLogin = async (username: string, password: string) => {
|
||||
const { host } = useLocalState.getState();
|
||||
|
||||
// Get token from server
|
||||
const token = await axios
|
||||
.get(`${host}${url(ApiPaths.user_token)}`, { auth: { username, password } })
|
||||
.then((response) => response.data.token)
|
||||
.catch((error) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
if (token === false) return token;
|
||||
|
||||
// log in with token
|
||||
doTokenLogin(token);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const doClassicLogout = async () => {
|
||||
// TODO @matmair - logout from the server session
|
||||
// Set token in context
|
||||
const { setToken } = useSessionState.getState();
|
||||
setToken(undefined);
|
||||
|
||||
notifications.show({
|
||||
title: t`Logout successfull`,
|
||||
message: t`See you soon.`,
|
||||
color: 'green',
|
||||
icon: <IconCheck size="1rem" />
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const doSimpleLogin = async (email: string) => {
|
||||
const { host } = useLocalState.getState();
|
||||
const mail = await axios
|
||||
.post(`${host}${url(ApiPaths.user_simple_login)}`, {
|
||||
email: email
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch((error) => {
|
||||
return false;
|
||||
});
|
||||
return mail;
|
||||
};
|
||||
|
||||
export const doTokenLogin = (token: string) => {
|
||||
const { setToken } = useSessionState.getState();
|
||||
const { fetchApiState } = useApiState.getState();
|
||||
|
||||
setToken(token);
|
||||
fetchApiState();
|
||||
};
|
||||
|
||||
export function handleReset(navigate: any, values: { email: string }) {
|
||||
api
|
||||
.post(url(ApiPaths.user_reset), values, {
|
||||
headers: { Authorization: '' }
|
||||
})
|
||||
.then((val) => {
|
||||
if (val.status === 200) {
|
||||
notifications.show({
|
||||
title: t`Mail delivery successfull`,
|
||||
message: t`Check your inbox for a reset link. This only works if you have an account. Check in spam too.`,
|
||||
color: 'green',
|
||||
autoClose: false
|
||||
});
|
||||
navigate('/login');
|
||||
} else {
|
||||
notifications.show({
|
||||
title: t`Reset failed`,
|
||||
message: t`Check your your input and try again.`,
|
||||
color: 'red'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function checkLoginState(navigate: any) {
|
||||
api
|
||||
.get(url(ApiPaths.user_token))
|
||||
.then((val) => {
|
||||
if (val.status === 200 && val.data.token) {
|
||||
doTokenLogin(val.data.token);
|
||||
|
||||
notifications.show({
|
||||
title: t`Already logged in`,
|
||||
message: t`Found an existing login - using it to log you in.`,
|
||||
color: 'green',
|
||||
icon: <IconCheck size="1rem" />
|
||||
});
|
||||
|
||||
navigate('/home');
|
||||
} else {
|
||||
navigate('/login');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
navigate('/login');
|
||||
});
|
||||
}
|
19
src/frontend/src/functions/loading.tsx
Normal file
19
src/frontend/src/functions/loading.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Center, Loader, Stack } from '@mantine/core';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
function LoadingFallback() {
|
||||
return (
|
||||
<Stack>
|
||||
<Center>
|
||||
<Loader />
|
||||
</Center>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export const Loadable = (Component: any) => (props: JSX.IntrinsicAttributes) =>
|
||||
(
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<Component {...props} />
|
||||
</Suspense>
|
||||
);
|
184
src/frontend/src/globalStyle.tsx
Normal file
184
src/frontend/src/globalStyle.tsx
Normal file
@ -0,0 +1,184 @@
|
||||
import { createStyles, rem } from '@mantine/core';
|
||||
|
||||
export const InvenTreeStyle = createStyles((theme) => ({
|
||||
layoutHeader: {
|
||||
paddingTop: theme.spacing.sm,
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[6]
|
||||
: theme.colors.gray[0],
|
||||
borderBottom: `1px solid ${
|
||||
theme.colorScheme === 'dark' ? 'transparent' : theme.colors.gray[2]
|
||||
}`,
|
||||
marginBottom: 10
|
||||
},
|
||||
|
||||
layoutFooter: {
|
||||
marginTop: 10,
|
||||
borderTop: `1px solid ${
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2]
|
||||
}`
|
||||
},
|
||||
|
||||
layoutHeaderSection: {
|
||||
paddingBottom: theme.spacing.sm
|
||||
},
|
||||
|
||||
layoutHeaderUser: {
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.black,
|
||||
padding: `${theme.spacing.xs}px ${theme.spacing.sm}px`,
|
||||
borderRadius: theme.defaultRadius,
|
||||
transition: 'background-color 100ms ease',
|
||||
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
|
||||
headerDropdownFooter: {
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[7]
|
||||
: theme.colors.gray[0],
|
||||
margin: `calc(${theme.spacing.md} * -1)`,
|
||||
marginTop: theme.spacing.sm,
|
||||
padding: `${theme.spacing.md} calc(${theme.spacing.md} * 2)`,
|
||||
paddingBottom: theme.spacing.xl,
|
||||
borderTop: `${rem(1)} solid ${
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1]
|
||||
}`
|
||||
},
|
||||
|
||||
link: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
paddingLeft: theme.spacing.md,
|
||||
paddingRight: theme.spacing.md,
|
||||
textDecoration: 'none',
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
fontWeight: 500,
|
||||
fontSize: theme.fontSizes.sm,
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
height: rem(42),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
...theme.fn.hover({
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[6]
|
||||
: theme.colors.gray[0]
|
||||
})
|
||||
},
|
||||
|
||||
subLink: {
|
||||
width: '100%',
|
||||
padding: `${theme.spacing.xs} ${theme.spacing.md}`,
|
||||
borderRadius: theme.defaultRadius,
|
||||
|
||||
...theme.fn.hover({
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[7]
|
||||
: theme.colors.gray[0]
|
||||
}),
|
||||
|
||||
'&:active': theme.activeStyles
|
||||
},
|
||||
|
||||
docHover: {
|
||||
border: `1px dashed `
|
||||
},
|
||||
|
||||
layoutContent: {
|
||||
flex: 1,
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
layoutFooterLinks: {
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
marginTop: theme.spacing.md
|
||||
}
|
||||
},
|
||||
|
||||
layoutFooterInner: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingTop: theme.spacing.xl,
|
||||
paddingBottom: theme.spacing.xl,
|
||||
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
flexDirection: 'column'
|
||||
}
|
||||
},
|
||||
|
||||
tabs: {
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
|
||||
tabsList: {
|
||||
borderBottom: '0 !important',
|
||||
'& > button:first-of-type': {
|
||||
paddingLeft: '0 !important'
|
||||
},
|
||||
|
||||
'& > button:last-of-type': {
|
||||
paddingRight: '0 !important'
|
||||
}
|
||||
},
|
||||
|
||||
tab: {
|
||||
fontWeight: 500,
|
||||
height: 38,
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[5]
|
||||
: theme.colors.gray[1]
|
||||
}
|
||||
},
|
||||
|
||||
signText: {
|
||||
fontSize: 'xl',
|
||||
fontWeight: 700
|
||||
},
|
||||
|
||||
error: {
|
||||
backgroundColor: theme.colors.gray[0],
|
||||
color: theme.colors.red[6]
|
||||
},
|
||||
|
||||
dashboardItemValue: {
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
lineHeight: 1
|
||||
},
|
||||
|
||||
dashboardItemTitle: {
|
||||
fontWeight: 700
|
||||
},
|
||||
|
||||
card: {
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white
|
||||
},
|
||||
|
||||
itemTopBorder: {
|
||||
borderTop: `1px solid ${
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[2]
|
||||
}`
|
||||
},
|
||||
|
||||
navigationDrawer: {
|
||||
padding: 0
|
||||
}
|
||||
}));
|
@ -12,3 +12,794 @@ msgstr ""
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/components/DashboardItemProxy.tsx:32
|
||||
#~ msgid "Title"
|
||||
#~ msgstr "Name"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:47
|
||||
msgid "Login failed"
|
||||
msgstr "Login fehlgeschlagen"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:48
|
||||
#: src/components/forms/AuthenticationForm.tsx:74
|
||||
#: src/functions/auth.tsx:83
|
||||
msgid "Check your your input and try again."
|
||||
msgstr "Überprüfen Sie Ihre Eingabe und versuchen Sie es erneut."
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:53
|
||||
msgid "Login successfull"
|
||||
msgstr "Anmeldung erfolgreich"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:54
|
||||
msgid "Welcome back!"
|
||||
msgstr "Willkommen zurück!"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:65
|
||||
#: src/functions/auth.tsx:74
|
||||
msgid "Mail delivery successfull"
|
||||
msgstr "Mail erfolgreich gesendet"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:66
|
||||
msgid "Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too."
|
||||
msgstr "Prüfen Sie Ihren Posteingang auf den Anmeldelink. Wenn Sie ein Konto haben, erhalten Sie einen Anmeldelink. Prüfen Sie auch den Spam."
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:73
|
||||
msgid "Input error"
|
||||
msgstr "Eingabefehler"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:95
|
||||
msgid "Username"
|
||||
msgstr "Nutzername"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:101
|
||||
#: src/pages/Auth/Set-Password.tsx:105
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:102
|
||||
msgid "Your password"
|
||||
msgstr "Dein Passwort"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:113
|
||||
#: src/pages/Auth/Reset.tsx:26
|
||||
msgid "Reset password"
|
||||
msgstr "Passwort zurücksetzen"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:121
|
||||
#: src/pages/Auth/Reset.tsx:31
|
||||
msgid "Email"
|
||||
msgstr "Mail"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:122
|
||||
#: src/pages/Auth/Reset.tsx:32
|
||||
#: src/pages/Auth/Set-Password.tsx:106
|
||||
msgid "We will send you a link to login - if you are registered"
|
||||
msgstr "Wir werden Ihnen einen Link für die Anmeldung senden"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:138
|
||||
msgid "Send me an email"
|
||||
msgstr "Mail erhalten"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:140
|
||||
msgid "I will use username and password"
|
||||
msgstr "Benutzername und Passwort verwenden"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:145
|
||||
msgid "Log in"
|
||||
msgstr "Einoggen"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:147
|
||||
#: src/pages/Auth/Reset.tsx:41
|
||||
#: src/pages/Auth/Set-Password.tsx:111
|
||||
msgid "Send mail"
|
||||
msgstr "Mail senden"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:36
|
||||
#: src/components/forms/HostOptionsForm.tsx:66
|
||||
msgid "Host"
|
||||
msgstr "Adresse"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:42
|
||||
#: src/components/forms/HostOptionsForm.tsx:69
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:74
|
||||
msgid "No one here..."
|
||||
msgstr "Kein Eintrag..."
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:85
|
||||
msgid "Add Host"
|
||||
msgstr "Adresse hinzufügen"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:89
|
||||
msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: src/components/items/DocTooltip.tsx:89
|
||||
msgid "Read More"
|
||||
msgstr "Mehr lesen"
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:5
|
||||
msgid "Unknown error"
|
||||
msgstr "Unbekannter Fehler"
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:10
|
||||
msgid "An error occurred:"
|
||||
msgstr "Ein Fehler ist aufgetreten:"
|
||||
|
||||
#: src/components/items/InvenTreeLogo.tsx:13
|
||||
msgid "InvenTree Logo"
|
||||
msgstr "InvenTree's Logo"
|
||||
|
||||
#: src/components/items/Placeholder.tsx:10
|
||||
msgid "This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/Placeholder.tsx:13
|
||||
msgid "PLH"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ScanButton.tsx:12
|
||||
msgid "Scan QR code"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:68
|
||||
msgid "Unknown response"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:98
|
||||
msgid "Error while getting camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:121
|
||||
msgid "Error while scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:135
|
||||
msgid "Error while stopping"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Not scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:155
|
||||
msgid "Select Camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:165
|
||||
msgid "Start scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:172
|
||||
msgid "Stop scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:177
|
||||
msgid "No scans yet!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:197
|
||||
msgid "Close modal"
|
||||
msgstr "Dialog schließen"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:53
|
||||
msgid "Notifications"
|
||||
msgstr "Benachrichtigungen"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:61
|
||||
#: src/pages/Index/Profile/Profile.tsx:15
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:65
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:68
|
||||
msgid "Current language {locale}"
|
||||
msgstr "Aktuelle Sprache {locale}"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:71
|
||||
msgid "Switch to pseudo language"
|
||||
msgstr "Zu Pseudo-Sprache wechseln"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:74
|
||||
msgid "Account settings"
|
||||
msgstr "Benutzereinstellungen"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:83
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:52
|
||||
msgid "Open Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:70
|
||||
msgid "View all"
|
||||
msgstr "Alle anzeigen"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:84
|
||||
#: src/components/nav/NavHoverMenu.tsx:94
|
||||
msgid "Get started"
|
||||
msgstr "Loslegen"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:87
|
||||
msgid "Overview over high-level objects, functions and possible usecases."
|
||||
msgstr "Übersicht über die wichtigsten Objekte, Funktionen und mögliche Anwendungsfälle."
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:59
|
||||
msgid "Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:62
|
||||
msgid "Pages"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:67
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:77
|
||||
msgid "Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:80
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:121
|
||||
msgid "Submit"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: src/defaults.tsx:23
|
||||
#: src/pages/Index/Part.tsx:12
|
||||
#~ msgid "Part"
|
||||
#~ msgstr "Teil"
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:8
|
||||
msgid "InvenTree Demo"
|
||||
msgstr "Demo von InvenTree"
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:16
|
||||
msgid "Local Server"
|
||||
msgstr "Lokaler Server"
|
||||
|
||||
#: src/defaults/links.tsx:8
|
||||
msgid "Website"
|
||||
msgstr "Webseite"
|
||||
|
||||
#: src/defaults/links.tsx:13
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/defaults/links.tsx:18
|
||||
msgid "Demo"
|
||||
msgstr "Demo"
|
||||
|
||||
#: src/defaults/links.tsx:22
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#: src/pages/Index/Home.tsx:12
|
||||
msgid "Home"
|
||||
msgstr "Startseite"
|
||||
|
||||
#: src/defaults/links.tsx:35
|
||||
msgid "Getting Started"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:36
|
||||
msgid "Getting started with InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:41
|
||||
msgid "API"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:42
|
||||
msgid "InvenTree API documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:47
|
||||
msgid "Developer Manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:48
|
||||
msgid "InvenTree developer manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:53
|
||||
msgid "FAQ"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:54
|
||||
msgid "Frequently asked questions"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:62
|
||||
msgid "Instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:63
|
||||
msgid "About this Inventree instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:68
|
||||
msgid "InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:69
|
||||
msgid "About the InvenTree org"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:74
|
||||
msgid "Licenses"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:75
|
||||
msgid "Licenses for packages used by InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:7
|
||||
#~ msgid "Open sourcea"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#~ msgid "Open source"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
msgid "Start page of your instance."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:12
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting and more and more and more"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:15
|
||||
msgid "Profile page"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:16
|
||||
msgid "User attributes and design settings."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:21
|
||||
#~ msgid "Free for everyone"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:22
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:24
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes in the intensity"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:32
|
||||
#~ msgid "abc"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:37
|
||||
#~ msgid "Random image"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:40
|
||||
#~ msgid "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Name liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assume. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:105
|
||||
#~ msgid "Yanma is capable of seeing 360 degrees without"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:109
|
||||
#~ msgid "Security"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:111
|
||||
#~ msgid "The shell’s rounded shape and the grooves on its."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:116
|
||||
#~ msgid "Analytics"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:118
|
||||
#~ msgid "This Pokémon uses its flying ability to quickly chase"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:125
|
||||
#~ msgid "Combusken battles with the intensely hot flames it spews"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:36
|
||||
msgid "Logout successfull"
|
||||
msgstr "Erfolgreich abgemeldet"
|
||||
|
||||
#: src/functions/auth.tsx:37
|
||||
msgid "See you soon."
|
||||
msgstr "Auf Wiedersehen."
|
||||
|
||||
#: src/functions/auth.tsx:75
|
||||
msgid "Check your inbox for a reset link. This only works if you have an account. Check in spam too."
|
||||
msgstr "Prüfen Sie Ihren Posteingang für einen Link zum Zurücksetzen. Dies funktioniert nur, wenn Sie ein Konto haben. Prüfen Sie auch den Spam-Ordner."
|
||||
|
||||
#: src/functions/auth.tsx:82
|
||||
#: src/pages/Auth/Set-Password.tsx:38
|
||||
msgid "Reset failed"
|
||||
msgstr "Zurücksetzen fehlgeschlagen"
|
||||
|
||||
#: src/functions/auth.tsx:98
|
||||
msgid "Already logged in"
|
||||
msgstr "Bereits angemeldet"
|
||||
|
||||
#: src/functions/auth.tsx:99
|
||||
msgid "Found an existing login - using it to log you in."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Logged-In.tsx:18
|
||||
msgid "Checking if you are already logged in"
|
||||
msgstr "Prüfe ob Sie bereits angemeldet sind"
|
||||
|
||||
#: src/pages/Auth/Login.tsx:20
|
||||
msgid "No selection"
|
||||
msgstr "Keine Auswahl"
|
||||
|
||||
#: src/pages/Auth/Login.tsx:121
|
||||
msgid "Edit host options"
|
||||
msgstr "Server konfigurieren"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:29
|
||||
msgid "Token invalid"
|
||||
msgstr "Token ungültig"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:30
|
||||
msgid "You need to provide a valid token to set a new password. Check your inbox for a reset link."
|
||||
msgstr "Sie müssen einen gültigen Token angeben, um ein neues Passwort festzulegen. Prüfen Sie Ihren Posteingang für einen Link zum Zurücksetzen."
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:48
|
||||
msgid "No token provided"
|
||||
msgstr "Kein Token angegeben"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:49
|
||||
msgid "You need to provide a token to set a new password. Check your inbox for a reset link."
|
||||
msgstr "Sie müssen einen Token angeben, um ein neues Passwort festzulegen. Prüfen Sie Ihren Posteingang für einen Link zum Zurücksetzen."
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:72
|
||||
msgid "Password set"
|
||||
msgstr "Passwort festgelegt"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:73
|
||||
msgid "The password was set successfully. You can now login with your new password"
|
||||
msgstr "Das Passwort wurde erfolgreich festgelegt. Sie können sich jetzt mit Ihrem neuen Passwort anmelden"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:100
|
||||
msgid "Set new password"
|
||||
msgstr "Passwort festlegen"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:12
|
||||
#: src/pages/ErrorPage.tsx:25
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:17
|
||||
msgid "Error: {0}"
|
||||
msgstr "Fehler: {0}"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:28
|
||||
msgid "Sorry, an unexpected error has occurred."
|
||||
msgstr "Es ist ein unerwarteter Fehler aufgetreten."
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:17
|
||||
#~ msgid "Subscribed Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:24
|
||||
#~ msgid "Subscribed Categories"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:31
|
||||
#~ msgid "Latest Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:38
|
||||
#~ msgid "BOM Waiting Validation"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:45
|
||||
#~ msgid "Recently Updated"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:52
|
||||
#~ msgid "Low Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:59
|
||||
#~ msgid "Depleted Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:66
|
||||
#~ msgid "Required for Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:73
|
||||
#~ msgid "Expired Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:80
|
||||
#~ msgid "Stale Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:87
|
||||
#~ msgid "Build Orders In Progress"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:94
|
||||
#~ msgid "Overdue Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:101
|
||||
#~ msgid "Outstanding Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:108
|
||||
#~ msgid "Overdue Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:115
|
||||
#~ msgid "Outstanding Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:122
|
||||
#~ msgid "Overdue Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:129
|
||||
#~ msgid "Current News"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:140
|
||||
#~ msgid "Dashboard"
|
||||
#~ msgstr "Übersicht"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:143
|
||||
#~ msgid "Autoupdate"
|
||||
#~ msgstr "Automatisch aktualisieren"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:23
|
||||
msgid "User"
|
||||
msgstr "Nutzer"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:27
|
||||
#: src/pages/Index/Profile/Profile.tsx:46
|
||||
#~ msgid "User Settings"
|
||||
#~ msgstr "Benutzereinstellungen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:30
|
||||
#: src/pages/Index/Profile/Profile.tsx:141
|
||||
#~ msgid "Notification Settings"
|
||||
#~ msgstr "Benachrichtigungseinstellungen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:33
|
||||
#~ msgid "Global Settings"
|
||||
#~ msgstr "Servereinstellungen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:36
|
||||
#: src/pages/Index/Profile/Profile.tsx:312
|
||||
#: src/pages/Index/Profile/Profile.tsx:329
|
||||
#~ msgid "Plugin Settings"
|
||||
#~ msgstr "Plugineinstellungen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:47
|
||||
#~ msgid "Settings for the current user"
|
||||
#~ msgstr "Einstellungen für den aktuellen Benutzer"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:51
|
||||
#~ msgid "Home Page Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:76
|
||||
#~ msgid "Search Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:115
|
||||
#: src/pages/Index/Profile/Profile.tsx:211
|
||||
#~ msgid "Label Settings"
|
||||
#~ msgstr "Etikettendruck"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:120
|
||||
#: src/pages/Index/Profile/Profile.tsx:219
|
||||
#~ msgid "Report Settings"
|
||||
#~ msgstr "Berichte"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:125
|
||||
#~ msgid "Display Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:142
|
||||
#~ msgid "Settings for the notifications"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:148
|
||||
#~ msgid "Global Server Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:149
|
||||
#~ msgid "Global Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:153
|
||||
#~ msgid "Server Settings"
|
||||
#~ msgstr "Serverkonfiguration"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:187
|
||||
#~ msgid "Login Settings"
|
||||
#~ msgstr "Anmeldeeinstellungen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:202
|
||||
#~ msgid "Barcode Settings"
|
||||
#~ msgstr "Barcode-Feature verwenden"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:230
|
||||
#~ msgid "Part Settings"
|
||||
#~ msgstr "Teile"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:255
|
||||
#~ msgid "Pricing Settings"
|
||||
#~ msgstr "Bepreisung"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:270
|
||||
#~ msgid "Stock Settings"
|
||||
#~ msgstr "Bestand"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:284
|
||||
#~ msgid "Build Order Settings"
|
||||
#~ msgstr "Bauaufträge"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:289
|
||||
#~ msgid "Purchase Order Settings"
|
||||
#~ msgstr "Bestellugnen"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:300
|
||||
#~ msgid "Sales Order Settings"
|
||||
#~ msgstr "Aufträge"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:330
|
||||
#~ msgid "Plugin Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:27
|
||||
#~ msgid "Data is current beeing loaded"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:69
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:76
|
||||
#~ msgid "Failed to load"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:100
|
||||
#~ msgid "Show internal names"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:148
|
||||
#~ msgid "Input {0} is not known"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:161
|
||||
#~ msgid "Saved changes {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:162
|
||||
#~ msgid "Changed to {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:176
|
||||
#~ msgid "Error while saving {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:177
|
||||
#~ msgid "Error was {err}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:257
|
||||
#~ msgid "Plugin: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:262
|
||||
#~ msgid "Method: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:79
|
||||
msgid "Userinfo"
|
||||
msgstr "Benutzerdetails"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:92
|
||||
msgid "First name: {0}"
|
||||
msgstr "Vorname: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:103
|
||||
msgid "Last name: {0}"
|
||||
msgstr "Nachname: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:114
|
||||
msgid "Username: {0}"
|
||||
msgstr "Benutzername: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:71
|
||||
msgid "bars"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:72
|
||||
msgid "oval"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:73
|
||||
msgid "dots"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:84
|
||||
msgid "Design <0/>"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:92
|
||||
msgid "Primary color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:105
|
||||
msgid "White color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:113
|
||||
msgid "Black color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:121
|
||||
msgid "Border Radius"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:137
|
||||
msgid "Loader"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Logged-In.tsx:24
|
||||
#~ msgid "Found an exsisting login - using it to log you in."
|
||||
#~ msgstr "Eine bestehende Anmeldung wurde gefunden - diese wird verwendet um Sie anzumelden."
|
||||
|
||||
#: src/pages/NotFound.tsx:17
|
||||
msgid "Not Found"
|
||||
msgstr "Nicht gefunden"
|
||||
|
||||
#: src/pages/NotFound.tsx:20
|
||||
msgid "Sorry, this page is not known or was moved."
|
||||
msgstr "Diese Seite ist nicht bekannt oder wurde verschoben."
|
||||
|
||||
#: src/pages/NotFound.tsx:27
|
||||
msgid "Go to the start page"
|
||||
msgstr "Zur Startseite"
|
||||
|
||||
#: src/views/MobileAppView.tsx:14
|
||||
msgid "Mobile viewport detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:17
|
||||
msgid "Platform UI is optimized for Tablets and Desktops, you can use the official app for a mobile experience."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:23
|
||||
msgid "Read the docs"
|
||||
msgstr ""
|
||||
|
File diff suppressed because one or more lines are too long
@ -12,3 +12,794 @@ msgstr ""
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/components/DashboardItemProxy.tsx:32
|
||||
#~ msgid "Title"
|
||||
#~ msgstr "Title"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:47
|
||||
msgid "Login failed"
|
||||
msgstr "Login failed"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:48
|
||||
#: src/components/forms/AuthenticationForm.tsx:74
|
||||
#: src/functions/auth.tsx:83
|
||||
msgid "Check your your input and try again."
|
||||
msgstr "Check your your input and try again."
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:53
|
||||
msgid "Login successfull"
|
||||
msgstr "Login successfull"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:54
|
||||
msgid "Welcome back!"
|
||||
msgstr "Welcome back!"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:65
|
||||
#: src/functions/auth.tsx:74
|
||||
msgid "Mail delivery successfull"
|
||||
msgstr "Mail delivery successfull"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:66
|
||||
msgid "Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too."
|
||||
msgstr "Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too."
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:73
|
||||
msgid "Input error"
|
||||
msgstr "Input error"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:95
|
||||
msgid "Username"
|
||||
msgstr "Username"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:101
|
||||
#: src/pages/Auth/Set-Password.tsx:105
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:102
|
||||
msgid "Your password"
|
||||
msgstr "Your password"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:113
|
||||
#: src/pages/Auth/Reset.tsx:26
|
||||
msgid "Reset password"
|
||||
msgstr "Reset password"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:121
|
||||
#: src/pages/Auth/Reset.tsx:31
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:122
|
||||
#: src/pages/Auth/Reset.tsx:32
|
||||
#: src/pages/Auth/Set-Password.tsx:106
|
||||
msgid "We will send you a link to login - if you are registered"
|
||||
msgstr "We will send you a link to login - if you are registered"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:138
|
||||
msgid "Send me an email"
|
||||
msgstr "Send me an email"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:140
|
||||
msgid "I will use username and password"
|
||||
msgstr "I will use username and password"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:145
|
||||
msgid "Log in"
|
||||
msgstr "Log in"
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:147
|
||||
#: src/pages/Auth/Reset.tsx:41
|
||||
#: src/pages/Auth/Set-Password.tsx:111
|
||||
msgid "Send mail"
|
||||
msgstr "Send mail"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:36
|
||||
#: src/components/forms/HostOptionsForm.tsx:66
|
||||
msgid "Host"
|
||||
msgstr "Host"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:42
|
||||
#: src/components/forms/HostOptionsForm.tsx:69
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:74
|
||||
msgid "No one here..."
|
||||
msgstr "No one here..."
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:85
|
||||
msgid "Add Host"
|
||||
msgstr "Add Host"
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:89
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
#: src/components/items/DocTooltip.tsx:89
|
||||
msgid "Read More"
|
||||
msgstr "Read More"
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:5
|
||||
msgid "Unknown error"
|
||||
msgstr "Unknown error"
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:10
|
||||
msgid "An error occurred:"
|
||||
msgstr "An error occurred:"
|
||||
|
||||
#: src/components/items/InvenTreeLogo.tsx:13
|
||||
msgid "InvenTree Logo"
|
||||
msgstr "InvenTree Logo"
|
||||
|
||||
#: src/components/items/Placeholder.tsx:10
|
||||
msgid "This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing."
|
||||
msgstr "This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing."
|
||||
|
||||
#: src/components/items/Placeholder.tsx:13
|
||||
msgid "PLH"
|
||||
msgstr "PLH"
|
||||
|
||||
#: src/components/items/ScanButton.tsx:12
|
||||
msgid "Scan QR code"
|
||||
msgstr "Scan QR code"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:68
|
||||
msgid "Unknown response"
|
||||
msgstr "Unknown response"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:98
|
||||
msgid "Error while getting camera"
|
||||
msgstr "Error while getting camera"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:121
|
||||
msgid "Error while scanning"
|
||||
msgstr "Error while scanning"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:135
|
||||
msgid "Error while stopping"
|
||||
msgstr "Error while stopping"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Scanning"
|
||||
msgstr "Scanning"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Not scanning"
|
||||
msgstr "Not scanning"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:155
|
||||
msgid "Select Camera"
|
||||
msgstr "Select Camera"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:165
|
||||
msgid "Start scanning"
|
||||
msgstr "Start scanning"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:172
|
||||
msgid "Stop scanning"
|
||||
msgstr "Stop scanning"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:177
|
||||
msgid "No scans yet!"
|
||||
msgstr "No scans yet!"
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:197
|
||||
msgid "Close modal"
|
||||
msgstr "Close modal"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:53
|
||||
msgid "Notifications"
|
||||
msgstr "Notifications"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:61
|
||||
#: src/pages/Index/Profile/Profile.tsx:15
|
||||
msgid "Profile"
|
||||
msgstr "Profile"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:65
|
||||
msgid "Settings"
|
||||
msgstr "Settings"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:68
|
||||
msgid "Current language {locale}"
|
||||
msgstr "Current language {locale}"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:71
|
||||
msgid "Switch to pseudo language"
|
||||
msgstr "Switch to pseudo language"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:74
|
||||
msgid "Account settings"
|
||||
msgstr "Account settings"
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:83
|
||||
msgid "Logout"
|
||||
msgstr "Logout"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:52
|
||||
msgid "Open Navigation"
|
||||
msgstr "Open Navigation"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:70
|
||||
msgid "View all"
|
||||
msgstr "View all"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:84
|
||||
#: src/components/nav/NavHoverMenu.tsx:94
|
||||
msgid "Get started"
|
||||
msgstr "Get started"
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:87
|
||||
msgid "Overview over high-level objects, functions and possible usecases."
|
||||
msgstr "Overview over high-level objects, functions and possible usecases."
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:59
|
||||
msgid "Navigation"
|
||||
msgstr "Navigation"
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:62
|
||||
msgid "Pages"
|
||||
msgstr "Pages"
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:67
|
||||
msgid "Plugins"
|
||||
msgstr "Plugins"
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:77
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:80
|
||||
msgid "About"
|
||||
msgstr "About"
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:121
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
#: src/defaults.tsx:23
|
||||
#: src/pages/Index/Part.tsx:12
|
||||
#~ msgid "Part"
|
||||
#~ msgstr "Part"
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:8
|
||||
msgid "InvenTree Demo"
|
||||
msgstr "InvenTree Demo"
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:16
|
||||
msgid "Local Server"
|
||||
msgstr "Local Server"
|
||||
|
||||
#: src/defaults/links.tsx:8
|
||||
msgid "Website"
|
||||
msgstr "Website"
|
||||
|
||||
#: src/defaults/links.tsx:13
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/defaults/links.tsx:18
|
||||
msgid "Demo"
|
||||
msgstr "Demo"
|
||||
|
||||
#: src/defaults/links.tsx:22
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#: src/pages/Index/Home.tsx:12
|
||||
msgid "Home"
|
||||
msgstr "Home"
|
||||
|
||||
#: src/defaults/links.tsx:35
|
||||
msgid "Getting Started"
|
||||
msgstr "Getting Started"
|
||||
|
||||
#: src/defaults/links.tsx:36
|
||||
msgid "Getting started with InvenTree"
|
||||
msgstr "Getting started with InvenTree"
|
||||
|
||||
#: src/defaults/links.tsx:41
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/defaults/links.tsx:42
|
||||
msgid "InvenTree API documentation"
|
||||
msgstr "InvenTree API documentation"
|
||||
|
||||
#: src/defaults/links.tsx:47
|
||||
msgid "Developer Manual"
|
||||
msgstr "Developer Manual"
|
||||
|
||||
#: src/defaults/links.tsx:48
|
||||
msgid "InvenTree developer manual"
|
||||
msgstr "InvenTree developer manual"
|
||||
|
||||
#: src/defaults/links.tsx:53
|
||||
msgid "FAQ"
|
||||
msgstr "FAQ"
|
||||
|
||||
#: src/defaults/links.tsx:54
|
||||
msgid "Frequently asked questions"
|
||||
msgstr "Frequently asked questions"
|
||||
|
||||
#: src/defaults/links.tsx:62
|
||||
msgid "Instance"
|
||||
msgstr "Instance"
|
||||
|
||||
#: src/defaults/links.tsx:63
|
||||
msgid "About this Inventree instance"
|
||||
msgstr "About this Inventree instance"
|
||||
|
||||
#: src/defaults/links.tsx:68
|
||||
msgid "InvenTree"
|
||||
msgstr "InvenTree"
|
||||
|
||||
#: src/defaults/links.tsx:69
|
||||
msgid "About the InvenTree org"
|
||||
msgstr "About the InvenTree org"
|
||||
|
||||
#: src/defaults/links.tsx:74
|
||||
msgid "Licenses"
|
||||
msgstr "Licenses"
|
||||
|
||||
#: src/defaults/links.tsx:75
|
||||
msgid "Licenses for packages used by InvenTree"
|
||||
msgstr "Licenses for packages used by InvenTree"
|
||||
|
||||
#: src/defaults/menuItems.tsx:7
|
||||
#~ msgid "Open sourcea"
|
||||
#~ msgstr "Open sourcea"
|
||||
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#~ msgid "Open source"
|
||||
#~ msgstr "Open source"
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
msgid "Start page of your instance."
|
||||
msgstr "Start page of your instance."
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting"
|
||||
#~ msgstr "This Pokémon’s cry is very loud and distracting"
|
||||
|
||||
#: src/defaults/menuItems.tsx:12
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting and more and more and more"
|
||||
#~ msgstr "This Pokémon’s cry is very loud and distracting and more and more and more"
|
||||
|
||||
#: src/defaults/menuItems.tsx:15
|
||||
msgid "Profile page"
|
||||
msgstr "Profile page"
|
||||
|
||||
#: src/defaults/menuItems.tsx:16
|
||||
msgid "User attributes and design settings."
|
||||
msgstr "User attributes and design settings."
|
||||
|
||||
#: src/defaults/menuItems.tsx:21
|
||||
#~ msgid "Free for everyone"
|
||||
#~ msgstr "Free for everyone"
|
||||
|
||||
#: src/defaults/menuItems.tsx:22
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes"
|
||||
#~ msgstr "The fluid of Smeargle’s tail secretions changes"
|
||||
|
||||
#: src/defaults/menuItems.tsx:24
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes in the intensity"
|
||||
#~ msgstr "The fluid of Smeargle’s tail secretions changes in the intensity"
|
||||
|
||||
#: src/defaults/menuItems.tsx:32
|
||||
#~ msgid "abc"
|
||||
#~ msgstr "abc"
|
||||
|
||||
#: src/defaults/menuItems.tsx:37
|
||||
#~ msgid "Random image"
|
||||
#~ msgstr "Random image"
|
||||
|
||||
#: src/defaults/menuItems.tsx:40
|
||||
#~ msgid "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Name liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assume. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor"
|
||||
#~ msgstr "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Name liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assume. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor"
|
||||
|
||||
#: src/defaults/menuItems.tsx:105
|
||||
#~ msgid "Yanma is capable of seeing 360 degrees without"
|
||||
#~ msgstr "Yanma is capable of seeing 360 degrees without"
|
||||
|
||||
#: src/defaults/menuItems.tsx:109
|
||||
#~ msgid "Security"
|
||||
#~ msgstr "Security"
|
||||
|
||||
#: src/defaults/menuItems.tsx:111
|
||||
#~ msgid "The shell’s rounded shape and the grooves on its."
|
||||
#~ msgstr "The shell’s rounded shape and the grooves on its."
|
||||
|
||||
#: src/defaults/menuItems.tsx:116
|
||||
#~ msgid "Analytics"
|
||||
#~ msgstr "Analytics"
|
||||
|
||||
#: src/defaults/menuItems.tsx:118
|
||||
#~ msgid "This Pokémon uses its flying ability to quickly chase"
|
||||
#~ msgstr "This Pokémon uses its flying ability to quickly chase"
|
||||
|
||||
#: src/defaults/menuItems.tsx:125
|
||||
#~ msgid "Combusken battles with the intensely hot flames it spews"
|
||||
#~ msgstr "Combusken battles with the intensely hot flames it spews"
|
||||
|
||||
#: src/functions/auth.tsx:36
|
||||
msgid "Logout successfull"
|
||||
msgstr "Logout successfull"
|
||||
|
||||
#: src/functions/auth.tsx:37
|
||||
msgid "See you soon."
|
||||
msgstr "See you soon."
|
||||
|
||||
#: src/functions/auth.tsx:75
|
||||
msgid "Check your inbox for a reset link. This only works if you have an account. Check in spam too."
|
||||
msgstr "Check your inbox for a reset link. This only works if you have an account. Check in spam too."
|
||||
|
||||
#: src/functions/auth.tsx:82
|
||||
#: src/pages/Auth/Set-Password.tsx:38
|
||||
msgid "Reset failed"
|
||||
msgstr "Reset failed"
|
||||
|
||||
#: src/functions/auth.tsx:98
|
||||
msgid "Already logged in"
|
||||
msgstr "Already logged in"
|
||||
|
||||
#: src/functions/auth.tsx:99
|
||||
msgid "Found an existing login - using it to log you in."
|
||||
msgstr "Found an existing login - using it to log you in."
|
||||
|
||||
#: src/pages/Auth/Logged-In.tsx:18
|
||||
msgid "Checking if you are already logged in"
|
||||
msgstr "Checking if you are already logged in"
|
||||
|
||||
#: src/pages/Auth/Login.tsx:20
|
||||
msgid "No selection"
|
||||
msgstr "No selection"
|
||||
|
||||
#: src/pages/Auth/Login.tsx:121
|
||||
msgid "Edit host options"
|
||||
msgstr "Edit host options"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:29
|
||||
msgid "Token invalid"
|
||||
msgstr "Token invalid"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:30
|
||||
msgid "You need to provide a valid token to set a new password. Check your inbox for a reset link."
|
||||
msgstr "You need to provide a valid token to set a new password. Check your inbox for a reset link."
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:48
|
||||
msgid "No token provided"
|
||||
msgstr "No token provided"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:49
|
||||
msgid "You need to provide a token to set a new password. Check your inbox for a reset link."
|
||||
msgstr "You need to provide a token to set a new password. Check your inbox for a reset link."
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:72
|
||||
msgid "Password set"
|
||||
msgstr "Password set"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:73
|
||||
msgid "The password was set successfully. You can now login with your new password"
|
||||
msgstr "The password was set successfully. You can now login with your new password"
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:100
|
||||
msgid "Set new password"
|
||||
msgstr "Set new password"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:12
|
||||
#: src/pages/ErrorPage.tsx:25
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:17
|
||||
msgid "Error: {0}"
|
||||
msgstr "Error: {0}"
|
||||
|
||||
#: src/pages/ErrorPage.tsx:28
|
||||
msgid "Sorry, an unexpected error has occurred."
|
||||
msgstr "Sorry, an unexpected error has occurred."
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:17
|
||||
#~ msgid "Subscribed Parts"
|
||||
#~ msgstr "Subscribed Parts"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:24
|
||||
#~ msgid "Subscribed Categories"
|
||||
#~ msgstr "Subscribed Categories"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:31
|
||||
#~ msgid "Latest Parts"
|
||||
#~ msgstr "Latest Parts"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:38
|
||||
#~ msgid "BOM Waiting Validation"
|
||||
#~ msgstr "BOM Waiting Validation"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:45
|
||||
#~ msgid "Recently Updated"
|
||||
#~ msgstr "Recently Updated"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:52
|
||||
#~ msgid "Low Stock"
|
||||
#~ msgstr "Low Stock"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:59
|
||||
#~ msgid "Depleted Stock"
|
||||
#~ msgstr "Depleted Stock"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:66
|
||||
#~ msgid "Required for Build Orders"
|
||||
#~ msgstr "Required for Build Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:73
|
||||
#~ msgid "Expired Stock"
|
||||
#~ msgstr "Expired Stock"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:80
|
||||
#~ msgid "Stale Stock"
|
||||
#~ msgstr "Stale Stock"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:87
|
||||
#~ msgid "Build Orders In Progress"
|
||||
#~ msgstr "Build Orders In Progress"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:94
|
||||
#~ msgid "Overdue Build Orders"
|
||||
#~ msgstr "Overdue Build Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:101
|
||||
#~ msgid "Outstanding Purchase Orders"
|
||||
#~ msgstr "Outstanding Purchase Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:108
|
||||
#~ msgid "Overdue Purchase Orders"
|
||||
#~ msgstr "Overdue Purchase Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:115
|
||||
#~ msgid "Outstanding Sales Orders"
|
||||
#~ msgstr "Outstanding Sales Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:122
|
||||
#~ msgid "Overdue Sales Orders"
|
||||
#~ msgstr "Overdue Sales Orders"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:129
|
||||
#~ msgid "Current News"
|
||||
#~ msgstr "Current News"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:140
|
||||
#~ msgid "Dashboard"
|
||||
#~ msgstr "Dashboard"
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:143
|
||||
#~ msgid "Autoupdate"
|
||||
#~ msgstr "Autoupdate"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:23
|
||||
msgid "User"
|
||||
msgstr "User"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:27
|
||||
#: src/pages/Index/Profile/Profile.tsx:46
|
||||
#~ msgid "User Settings"
|
||||
#~ msgstr "User Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:30
|
||||
#: src/pages/Index/Profile/Profile.tsx:141
|
||||
#~ msgid "Notification Settings"
|
||||
#~ msgstr "Notification Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:33
|
||||
#~ msgid "Global Settings"
|
||||
#~ msgstr "Global Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:36
|
||||
#: src/pages/Index/Profile/Profile.tsx:312
|
||||
#: src/pages/Index/Profile/Profile.tsx:329
|
||||
#~ msgid "Plugin Settings"
|
||||
#~ msgstr "Plugin Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:47
|
||||
#~ msgid "Settings for the current user"
|
||||
#~ msgstr "Settings for the current user"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:51
|
||||
#~ msgid "Home Page Settings"
|
||||
#~ msgstr "Home Page Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:76
|
||||
#~ msgid "Search Settings"
|
||||
#~ msgstr "Search Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:115
|
||||
#: src/pages/Index/Profile/Profile.tsx:211
|
||||
#~ msgid "Label Settings"
|
||||
#~ msgstr "Label Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:120
|
||||
#: src/pages/Index/Profile/Profile.tsx:219
|
||||
#~ msgid "Report Settings"
|
||||
#~ msgstr "Report Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:125
|
||||
#~ msgid "Display Settings"
|
||||
#~ msgstr "Display Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:142
|
||||
#~ msgid "Settings for the notifications"
|
||||
#~ msgstr "Settings for the notifications"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:148
|
||||
#~ msgid "Global Server Settings"
|
||||
#~ msgstr "Global Server Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:149
|
||||
#~ msgid "Global Settings for this instance"
|
||||
#~ msgstr "Global Settings for this instance"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:153
|
||||
#~ msgid "Server Settings"
|
||||
#~ msgstr "Server Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:187
|
||||
#~ msgid "Login Settings"
|
||||
#~ msgstr "Login Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:202
|
||||
#~ msgid "Barcode Settings"
|
||||
#~ msgstr "Barcode Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:230
|
||||
#~ msgid "Part Settings"
|
||||
#~ msgstr "Part Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:255
|
||||
#~ msgid "Pricing Settings"
|
||||
#~ msgstr "Pricing Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:270
|
||||
#~ msgid "Stock Settings"
|
||||
#~ msgstr "Stock Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:284
|
||||
#~ msgid "Build Order Settings"
|
||||
#~ msgstr "Build Order Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:289
|
||||
#~ msgid "Purchase Order Settings"
|
||||
#~ msgstr "Purchase Order Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:300
|
||||
#~ msgid "Sales Order Settings"
|
||||
#~ msgstr "Sales Order Settings"
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:330
|
||||
#~ msgid "Plugin Settings for this instance"
|
||||
#~ msgstr "Plugin Settings for this instance"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:27
|
||||
#~ msgid "Data is current beeing loaded"
|
||||
#~ msgstr "Data is current beeing loaded"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:69
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:76
|
||||
#~ msgid "Failed to load"
|
||||
#~ msgstr "Failed to load"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:100
|
||||
#~ msgid "Show internal names"
|
||||
#~ msgstr "Show internal names"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:148
|
||||
#~ msgid "Input {0} is not known"
|
||||
#~ msgstr "Input {0} is not known"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:161
|
||||
#~ msgid "Saved changes {0}"
|
||||
#~ msgstr "Saved changes {0}"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:162
|
||||
#~ msgid "Changed to {0}"
|
||||
#~ msgstr "Changed to {0}"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:176
|
||||
#~ msgid "Error while saving {0}"
|
||||
#~ msgstr "Error while saving {0}"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:177
|
||||
#~ msgid "Error was {err}"
|
||||
#~ msgstr "Error was {err}"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:257
|
||||
#~ msgid "Plugin: {0}"
|
||||
#~ msgstr "Plugin: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:262
|
||||
#~ msgid "Method: {0}"
|
||||
#~ msgstr "Method: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:79
|
||||
msgid "Userinfo"
|
||||
msgstr "Userinfo"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:92
|
||||
msgid "First name: {0}"
|
||||
msgstr "First name: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:103
|
||||
msgid "Last name: {0}"
|
||||
msgstr "Last name: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:114
|
||||
msgid "Username: {0}"
|
||||
msgstr "Username: {0}"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:71
|
||||
msgid "bars"
|
||||
msgstr "bars"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:72
|
||||
msgid "oval"
|
||||
msgstr "oval"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:73
|
||||
msgid "dots"
|
||||
msgstr "dots"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:84
|
||||
msgid "Design <0/>"
|
||||
msgstr "Design <0/>"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:92
|
||||
msgid "Primary color"
|
||||
msgstr "Primary color"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:105
|
||||
msgid "White color"
|
||||
msgstr "White color"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:113
|
||||
msgid "Black color"
|
||||
msgstr "Black color"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:121
|
||||
msgid "Border Radius"
|
||||
msgstr "Border Radius"
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:137
|
||||
msgid "Loader"
|
||||
msgstr "Loader"
|
||||
|
||||
#: src/pages/Logged-In.tsx:24
|
||||
#~ msgid "Found an exsisting login - using it to log you in."
|
||||
#~ msgstr "Found an exsisting login - using it to log you in."
|
||||
|
||||
#: src/pages/NotFound.tsx:17
|
||||
msgid "Not Found"
|
||||
msgstr "Not Found"
|
||||
|
||||
#: src/pages/NotFound.tsx:20
|
||||
msgid "Sorry, this page is not known or was moved."
|
||||
msgstr "Sorry, this page is not known or was moved."
|
||||
|
||||
#: src/pages/NotFound.tsx:27
|
||||
msgid "Go to the start page"
|
||||
msgstr "Go to the start page"
|
||||
|
||||
#: src/views/MobileAppView.tsx:14
|
||||
msgid "Mobile viewport detected"
|
||||
msgstr "Mobile viewport detected"
|
||||
|
||||
#: src/views/MobileAppView.tsx:17
|
||||
msgid "Platform UI is optimized for Tablets and Desktops, you can use the official app for a mobile experience."
|
||||
msgstr "Platform UI is optimized for Tablets and Desktops, you can use the official app for a mobile experience."
|
||||
|
||||
#: src/views/MobileAppView.tsx:23
|
||||
msgid "Read the docs"
|
||||
msgstr "Read the docs"
|
||||
|
File diff suppressed because one or more lines are too long
@ -12,3 +12,838 @@ msgstr ""
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:37
|
||||
#~ msgid "login"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:37
|
||||
#~ msgid "register"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:56
|
||||
#~ msgid "Welcome {actionname} to"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:61
|
||||
#~ msgid "Placeholder"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:64
|
||||
#~ msgid "Or continue with email"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:74
|
||||
#~ msgid "Your name"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:90
|
||||
#~ msgid "Invalid email"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:101
|
||||
#~ msgid "Password should include at least 6 characters"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:114
|
||||
#~ msgid "Already have an account? Login"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:115
|
||||
#~ msgid "Don't have an account? Register"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/DashboardItemProxy.tsx:32
|
||||
#~ msgid "Title"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:47
|
||||
msgid "Login failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:48
|
||||
#: src/components/forms/AuthenticationForm.tsx:74
|
||||
#: src/functions/auth.tsx:83
|
||||
msgid "Check your your input and try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:53
|
||||
msgid "Login successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:54
|
||||
msgid "Welcome back!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:65
|
||||
#: src/functions/auth.tsx:74
|
||||
msgid "Mail delivery successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:66
|
||||
msgid "Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:73
|
||||
msgid "Input error"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:95
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:101
|
||||
#: src/pages/Auth/Set-Password.tsx:105
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:102
|
||||
msgid "Your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:113
|
||||
#: src/pages/Auth/Reset.tsx:26
|
||||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:121
|
||||
#: src/pages/Auth/Reset.tsx:31
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:122
|
||||
#: src/pages/Auth/Reset.tsx:32
|
||||
#: src/pages/Auth/Set-Password.tsx:106
|
||||
msgid "We will send you a link to login - if you are registered"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:138
|
||||
msgid "Send me an email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:140
|
||||
msgid "I will use username and password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:145
|
||||
msgid "Log in"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:147
|
||||
#: src/pages/Auth/Reset.tsx:41
|
||||
#: src/pages/Auth/Set-Password.tsx:111
|
||||
msgid "Send mail"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:36
|
||||
#: src/components/forms/HostOptionsForm.tsx:66
|
||||
msgid "Host"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:42
|
||||
#: src/components/forms/HostOptionsForm.tsx:69
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:74
|
||||
msgid "No one here..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:85
|
||||
msgid "Add Host"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:89
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/DocTooltip.tsx:89
|
||||
msgid "Read More"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:5
|
||||
msgid "Unknown error"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:6
|
||||
#~ msgid "An error occured:"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:10
|
||||
msgid "An error occurred:"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/InvenTreeLogo.tsx:13
|
||||
msgid "InvenTree Logo"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/Placeholder.tsx:10
|
||||
msgid "This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/Placeholder.tsx:13
|
||||
msgid "PLH"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ScanButton.tsx:12
|
||||
msgid "Scan QR code"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:68
|
||||
msgid "Unknown response"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:98
|
||||
msgid "Error while getting camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:121
|
||||
msgid "Error while scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:135
|
||||
msgid "Error while stopping"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Not scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:155
|
||||
msgid "Select Camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:165
|
||||
msgid "Start scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:172
|
||||
msgid "Stop scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:177
|
||||
msgid "No scans yet!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:197
|
||||
msgid "Close modal"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:53
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:61
|
||||
#: src/pages/Index/Profile/Profile.tsx:15
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:65
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:68
|
||||
msgid "Current language {locale}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:71
|
||||
msgid "Switch to pseudo language"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:74
|
||||
msgid "Account settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:83
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:52
|
||||
msgid "Open Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:70
|
||||
msgid "View all"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:84
|
||||
#: src/components/nav/NavHoverMenu.tsx:94
|
||||
msgid "Get started"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:87
|
||||
msgid "Overview over high-level objects, functions and possible usecases."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:59
|
||||
msgid "Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:62
|
||||
msgid "Pages"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:67
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:77
|
||||
msgid "Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:80
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:121
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults.tsx:23
|
||||
#: src/pages/Index/Part.tsx:12
|
||||
#~ msgid "Part"
|
||||
#~ msgstr "Alkatrész"
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:8
|
||||
msgid "InvenTree Demo"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:16
|
||||
msgid "Local Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:8
|
||||
msgid "Website"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:13
|
||||
msgid "GitHub"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:18
|
||||
msgid "Demo"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:22
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#: src/pages/Index/Home.tsx:12
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:35
|
||||
msgid "Getting Started"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:36
|
||||
msgid "Getting started with InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:41
|
||||
msgid "API"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:42
|
||||
msgid "InvenTree API documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:47
|
||||
msgid "Developer Manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:48
|
||||
msgid "InvenTree developer manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:53
|
||||
msgid "FAQ"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:54
|
||||
msgid "Frequently asked questions"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:62
|
||||
msgid "Instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:63
|
||||
msgid "About this Inventree instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:68
|
||||
msgid "InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:69
|
||||
msgid "About the InvenTree org"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:74
|
||||
msgid "Licenses"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:75
|
||||
msgid "Licenses for packages used by InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:7
|
||||
#~ msgid "Open sourcea"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#~ msgid "Open source"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
msgid "Start page of your instance."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:12
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting and more and more and more"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:15
|
||||
msgid "Profile page"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:16
|
||||
msgid "User attributes and design settings."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:21
|
||||
#~ msgid "Free for everyone"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:22
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:24
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes in the intensity"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:32
|
||||
#~ msgid "abc"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:37
|
||||
#~ msgid "Random image"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:40
|
||||
#~ msgid "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Name liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assume. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:105
|
||||
#~ msgid "Yanma is capable of seeing 360 degrees without"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:109
|
||||
#~ msgid "Security"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:111
|
||||
#~ msgid "The shell’s rounded shape and the grooves on its."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:116
|
||||
#~ msgid "Analytics"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:118
|
||||
#~ msgid "This Pokémon uses its flying ability to quickly chase"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:125
|
||||
#~ msgid "Combusken battles with the intensely hot flames it spews"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:36
|
||||
msgid "Logout successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:37
|
||||
msgid "See you soon."
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:75
|
||||
msgid "Check your inbox for a reset link. This only works if you have an account. Check in spam too."
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:82
|
||||
#: src/pages/Auth/Set-Password.tsx:38
|
||||
msgid "Reset failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:98
|
||||
msgid "Already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:99
|
||||
msgid "Found an existing login - using it to log you in."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Logged-In.tsx:18
|
||||
msgid "Checking if you are already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Login.tsx:20
|
||||
msgid "No selection"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Login.tsx:121
|
||||
msgid "Edit host options"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:29
|
||||
msgid "Token invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:30
|
||||
msgid "You need to provide a valid token to set a new password. Check your inbox for a reset link."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:48
|
||||
msgid "No token provided"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:49
|
||||
msgid "You need to provide a token to set a new password. Check your inbox for a reset link."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:72
|
||||
msgid "Password set"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:73
|
||||
msgid "The password was set successfully. You can now login with your new password"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:100
|
||||
msgid "Set new password"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:12
|
||||
#: src/pages/ErrorPage.tsx:25
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:17
|
||||
msgid "Error: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:28
|
||||
msgid "Sorry, an unexpected error has occurred."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:17
|
||||
#~ msgid "Subscribed Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:24
|
||||
#~ msgid "Subscribed Categories"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:31
|
||||
#~ msgid "Latest Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:38
|
||||
#~ msgid "BOM Waiting Validation"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:45
|
||||
#~ msgid "Recently Updated"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:52
|
||||
#~ msgid "Low Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:59
|
||||
#~ msgid "Depleted Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:66
|
||||
#~ msgid "Required for Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:73
|
||||
#~ msgid "Expired Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:80
|
||||
#~ msgid "Stale Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:87
|
||||
#~ msgid "Build Orders In Progress"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:94
|
||||
#~ msgid "Overdue Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:101
|
||||
#~ msgid "Outstanding Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:108
|
||||
#~ msgid "Overdue Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:115
|
||||
#~ msgid "Outstanding Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:122
|
||||
#~ msgid "Overdue Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:129
|
||||
#~ msgid "Current News"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:140
|
||||
#~ msgid "Dashboard"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:143
|
||||
#~ msgid "Autoupdate"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:23
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:27
|
||||
#: src/pages/Index/Profile/Profile.tsx:46
|
||||
#~ msgid "User Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:30
|
||||
#: src/pages/Index/Profile/Profile.tsx:141
|
||||
#~ msgid "Notification Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:33
|
||||
#~ msgid "Global Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:36
|
||||
#: src/pages/Index/Profile/Profile.tsx:312
|
||||
#: src/pages/Index/Profile/Profile.tsx:329
|
||||
#~ msgid "Plugin Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:47
|
||||
#~ msgid "Settings for the current user"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:51
|
||||
#~ msgid "Home Page Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:76
|
||||
#~ msgid "Search Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:115
|
||||
#: src/pages/Index/Profile/Profile.tsx:211
|
||||
#~ msgid "Label Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:120
|
||||
#: src/pages/Index/Profile/Profile.tsx:219
|
||||
#~ msgid "Report Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:125
|
||||
#~ msgid "Display Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:142
|
||||
#~ msgid "Settings for the notifications"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:148
|
||||
#~ msgid "Global Server Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:149
|
||||
#~ msgid "Global Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:153
|
||||
#~ msgid "Server Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:187
|
||||
#~ msgid "Login Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:202
|
||||
#~ msgid "Barcode Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:230
|
||||
#~ msgid "Part Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:255
|
||||
#~ msgid "Pricing Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:270
|
||||
#~ msgid "Stock Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:284
|
||||
#~ msgid "Build Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:289
|
||||
#~ msgid "Purchase Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:300
|
||||
#~ msgid "Sales Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:330
|
||||
#~ msgid "Plugin Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:27
|
||||
#~ msgid "Data is current beeing loaded"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:69
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:76
|
||||
#~ msgid "Failed to load"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:100
|
||||
#~ msgid "Show internal names"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:148
|
||||
#~ msgid "Input {0} is not known"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:161
|
||||
#~ msgid "Saved changes {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:162
|
||||
#~ msgid "Changed to {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:176
|
||||
#~ msgid "Error while saving {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:177
|
||||
#~ msgid "Error was {err}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:257
|
||||
#~ msgid "Plugin: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:262
|
||||
#~ msgid "Method: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:79
|
||||
msgid "Userinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:92
|
||||
msgid "First name: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:103
|
||||
msgid "Last name: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:114
|
||||
msgid "Username: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:71
|
||||
msgid "bars"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:72
|
||||
msgid "oval"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:73
|
||||
msgid "dots"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:84
|
||||
msgid "Design <0/>"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:92
|
||||
msgid "Primary color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:105
|
||||
msgid "White color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:113
|
||||
msgid "Black color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:121
|
||||
msgid "Border Radius"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:137
|
||||
msgid "Loader"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Logged-In.tsx:24
|
||||
#~ msgid "Found an exsisting login - using it to log you in."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:17
|
||||
msgid "Not Found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:20
|
||||
msgid "Sorry, this page is not known or was moved."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:27
|
||||
msgid "Go to the start page"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:14
|
||||
msgid "Mobile viewport detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:17
|
||||
msgid "Platform UI is optimized for Tablets and Desktops, you can use the official app for a mobile experience."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:23
|
||||
msgid "Read the docs"
|
||||
msgstr ""
|
||||
|
File diff suppressed because one or more lines are too long
@ -12,3 +12,838 @@ msgstr ""
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:37
|
||||
#~ msgid "login"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:37
|
||||
#~ msgid "register"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:56
|
||||
#~ msgid "Welcome {actionname} to"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:61
|
||||
#~ msgid "Placeholder"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:64
|
||||
#~ msgid "Or continue with email"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:74
|
||||
#~ msgid "Your name"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:90
|
||||
#~ msgid "Invalid email"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:101
|
||||
#~ msgid "Password should include at least 6 characters"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:114
|
||||
#~ msgid "Already have an account? Login"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/AuthenticationForm.tsx:115
|
||||
#~ msgid "Don't have an account? Register"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/DashboardItemProxy.tsx:32
|
||||
#~ msgid "Title"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:47
|
||||
msgid "Login failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:48
|
||||
#: src/components/forms/AuthenticationForm.tsx:74
|
||||
#: src/functions/auth.tsx:83
|
||||
msgid "Check your your input and try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:53
|
||||
msgid "Login successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:54
|
||||
msgid "Welcome back!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:65
|
||||
#: src/functions/auth.tsx:74
|
||||
msgid "Mail delivery successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:66
|
||||
msgid "Check your inbox for the login link. If you have an account, you will receive a login link. Check in spam too."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:73
|
||||
msgid "Input error"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:95
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:101
|
||||
#: src/pages/Auth/Set-Password.tsx:105
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:102
|
||||
msgid "Your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:113
|
||||
#: src/pages/Auth/Reset.tsx:26
|
||||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:121
|
||||
#: src/pages/Auth/Reset.tsx:31
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:122
|
||||
#: src/pages/Auth/Reset.tsx:32
|
||||
#: src/pages/Auth/Set-Password.tsx:106
|
||||
msgid "We will send you a link to login - if you are registered"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:138
|
||||
msgid "Send me an email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:140
|
||||
msgid "I will use username and password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:145
|
||||
msgid "Log in"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/AuthenticationForm.tsx:147
|
||||
#: src/pages/Auth/Reset.tsx:41
|
||||
#: src/pages/Auth/Set-Password.tsx:111
|
||||
msgid "Send mail"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:36
|
||||
#: src/components/forms/HostOptionsForm.tsx:66
|
||||
msgid "Host"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:42
|
||||
#: src/components/forms/HostOptionsForm.tsx:69
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:74
|
||||
msgid "No one here..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:85
|
||||
msgid "Add Host"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/HostOptionsForm.tsx:89
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/DocTooltip.tsx:89
|
||||
msgid "Read More"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:5
|
||||
msgid "Unknown error"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:6
|
||||
#~ msgid "An error occured:"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/components/items/ErrorItem.tsx:10
|
||||
msgid "An error occurred:"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/InvenTreeLogo.tsx:13
|
||||
msgid "InvenTree Logo"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/Placeholder.tsx:10
|
||||
msgid "This feature/button/site is a placeholder for a feature that is not implemented, only partial or intended for testing."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/Placeholder.tsx:13
|
||||
msgid "PLH"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/items/ScanButton.tsx:12
|
||||
msgid "Scan QR code"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:68
|
||||
msgid "Unknown response"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:98
|
||||
msgid "Error while getting camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:121
|
||||
msgid "Error while scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:135
|
||||
msgid "Error while stopping"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:150
|
||||
msgid "Not scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:155
|
||||
msgid "Select Camera"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:165
|
||||
msgid "Start scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:172
|
||||
msgid "Stop scanning"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:177
|
||||
msgid "No scans yet!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/modals/QrCodeModal.tsx:197
|
||||
msgid "Close modal"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:53
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:61
|
||||
#: src/pages/Index/Profile/Profile.tsx:15
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:65
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:68
|
||||
msgid "Current language {locale}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:71
|
||||
msgid "Switch to pseudo language"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:74
|
||||
msgid "Account settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/MainMenu.tsx:83
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:52
|
||||
msgid "Open Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:70
|
||||
msgid "View all"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:84
|
||||
#: src/components/nav/NavHoverMenu.tsx:94
|
||||
msgid "Get started"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavHoverMenu.tsx:87
|
||||
msgid "Overview over high-level objects, functions and possible usecases."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:59
|
||||
msgid "Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:62
|
||||
msgid "Pages"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:67
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:77
|
||||
msgid "Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/nav/NavigationDrawer.tsx:80
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:121
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: src/contexts/ThemeContext.tsx:62
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults.tsx:23
|
||||
#: src/pages/Index/Part.tsx:12
|
||||
#~ msgid "Part"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:8
|
||||
msgid "InvenTree Demo"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/defaultHostList.tsx:16
|
||||
msgid "Local Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:8
|
||||
msgid "Website"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:13
|
||||
msgid "GitHub"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:18
|
||||
msgid "Demo"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:22
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#: src/pages/Index/Home.tsx:12
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:35
|
||||
msgid "Getting Started"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:36
|
||||
msgid "Getting started with InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:41
|
||||
msgid "API"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:42
|
||||
msgid "InvenTree API documentation"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:47
|
||||
msgid "Developer Manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:48
|
||||
msgid "InvenTree developer manual"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:53
|
||||
msgid "FAQ"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:54
|
||||
msgid "Frequently asked questions"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:62
|
||||
msgid "Instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:63
|
||||
msgid "About this Inventree instance"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:68
|
||||
msgid "InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:69
|
||||
msgid "About the InvenTree org"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:74
|
||||
msgid "Licenses"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/links.tsx:75
|
||||
msgid "Licenses for packages used by InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:7
|
||||
#~ msgid "Open sourcea"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:9
|
||||
#~ msgid "Open source"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
msgid "Start page of your instance."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:10
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:12
|
||||
#~ msgid "This Pokémon’s cry is very loud and distracting and more and more and more"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:15
|
||||
msgid "Profile page"
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:16
|
||||
msgid "User attributes and design settings."
|
||||
msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:21
|
||||
#~ msgid "Free for everyone"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:22
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:24
|
||||
#~ msgid "The fluid of Smeargle’s tail secretions changes in the intensity"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:32
|
||||
#~ msgid "abc"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:37
|
||||
#~ msgid "Random image"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:40
|
||||
#~ msgid "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore the feugait nulla facilisi. Name liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assume. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:105
|
||||
#~ msgid "Yanma is capable of seeing 360 degrees without"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:109
|
||||
#~ msgid "Security"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:111
|
||||
#~ msgid "The shell’s rounded shape and the grooves on its."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:116
|
||||
#~ msgid "Analytics"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:118
|
||||
#~ msgid "This Pokémon uses its flying ability to quickly chase"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/defaults/menuItems.tsx:125
|
||||
#~ msgid "Combusken battles with the intensely hot flames it spews"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:36
|
||||
msgid "Logout successfull"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:37
|
||||
msgid "See you soon."
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:75
|
||||
msgid "Check your inbox for a reset link. This only works if you have an account. Check in spam too."
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:82
|
||||
#: src/pages/Auth/Set-Password.tsx:38
|
||||
msgid "Reset failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:98
|
||||
msgid "Already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: src/functions/auth.tsx:99
|
||||
msgid "Found an existing login - using it to log you in."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Logged-In.tsx:18
|
||||
msgid "Checking if you are already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Login.tsx:20
|
||||
msgid "No selection"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Login.tsx:121
|
||||
msgid "Edit host options"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:29
|
||||
msgid "Token invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:30
|
||||
msgid "You need to provide a valid token to set a new password. Check your inbox for a reset link."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:48
|
||||
msgid "No token provided"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:49
|
||||
msgid "You need to provide a token to set a new password. Check your inbox for a reset link."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:72
|
||||
msgid "Password set"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:73
|
||||
msgid "The password was set successfully. You can now login with your new password"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Auth/Set-Password.tsx:100
|
||||
msgid "Set new password"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:12
|
||||
#: src/pages/ErrorPage.tsx:25
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:17
|
||||
msgid "Error: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/ErrorPage.tsx:28
|
||||
msgid "Sorry, an unexpected error has occurred."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:17
|
||||
#~ msgid "Subscribed Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:24
|
||||
#~ msgid "Subscribed Categories"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:31
|
||||
#~ msgid "Latest Parts"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:38
|
||||
#~ msgid "BOM Waiting Validation"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:45
|
||||
#~ msgid "Recently Updated"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:52
|
||||
#~ msgid "Low Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:59
|
||||
#~ msgid "Depleted Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:66
|
||||
#~ msgid "Required for Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:73
|
||||
#~ msgid "Expired Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:80
|
||||
#~ msgid "Stale Stock"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:87
|
||||
#~ msgid "Build Orders In Progress"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:94
|
||||
#~ msgid "Overdue Build Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:101
|
||||
#~ msgid "Outstanding Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:108
|
||||
#~ msgid "Overdue Purchase Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:115
|
||||
#~ msgid "Outstanding Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:122
|
||||
#~ msgid "Overdue Sales Orders"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:129
|
||||
#~ msgid "Current News"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:140
|
||||
#~ msgid "Dashboard"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Dashboard.tsx:143
|
||||
#~ msgid "Autoupdate"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:23
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:27
|
||||
#: src/pages/Index/Profile/Profile.tsx:46
|
||||
#~ msgid "User Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:30
|
||||
#: src/pages/Index/Profile/Profile.tsx:141
|
||||
#~ msgid "Notification Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:33
|
||||
#~ msgid "Global Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:36
|
||||
#: src/pages/Index/Profile/Profile.tsx:312
|
||||
#: src/pages/Index/Profile/Profile.tsx:329
|
||||
#~ msgid "Plugin Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:47
|
||||
#~ msgid "Settings for the current user"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:51
|
||||
#~ msgid "Home Page Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:76
|
||||
#~ msgid "Search Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:115
|
||||
#: src/pages/Index/Profile/Profile.tsx:211
|
||||
#~ msgid "Label Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:120
|
||||
#: src/pages/Index/Profile/Profile.tsx:219
|
||||
#~ msgid "Report Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:125
|
||||
#~ msgid "Display Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:142
|
||||
#~ msgid "Settings for the notifications"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:148
|
||||
#~ msgid "Global Server Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:149
|
||||
#~ msgid "Global Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:153
|
||||
#~ msgid "Server Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:187
|
||||
#~ msgid "Login Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:202
|
||||
#~ msgid "Barcode Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:230
|
||||
#~ msgid "Part Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:255
|
||||
#~ msgid "Pricing Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:270
|
||||
#~ msgid "Stock Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:284
|
||||
#~ msgid "Build Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:289
|
||||
#~ msgid "Purchase Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:300
|
||||
#~ msgid "Sales Order Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/Profile.tsx:330
|
||||
#~ msgid "Plugin Settings for this instance"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:27
|
||||
#~ msgid "Data is current beeing loaded"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:69
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:76
|
||||
#~ msgid "Failed to load"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:100
|
||||
#~ msgid "Show internal names"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:148
|
||||
#~ msgid "Input {0} is not known"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:161
|
||||
#~ msgid "Saved changes {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:162
|
||||
#~ msgid "Changed to {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:176
|
||||
#~ msgid "Error while saving {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:177
|
||||
#~ msgid "Error was {err}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:257
|
||||
#~ msgid "Plugin: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/SettingsPanel.tsx:262
|
||||
#~ msgid "Method: {0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:79
|
||||
msgid "Userinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:92
|
||||
msgid "First name: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:103
|
||||
msgid "Last name: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserPanel.tsx:114
|
||||
msgid "Username: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:71
|
||||
msgid "bars"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:72
|
||||
msgid "oval"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:73
|
||||
msgid "dots"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:84
|
||||
msgid "Design <0/>"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:92
|
||||
msgid "Primary color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:105
|
||||
msgid "White color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:113
|
||||
msgid "Black color"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:121
|
||||
msgid "Border Radius"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Index/Profile/UserTheme.tsx:137
|
||||
msgid "Loader"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Logged-In.tsx:24
|
||||
#~ msgid "Found an exsisting login - using it to log you in."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:17
|
||||
msgid "Not Found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:20
|
||||
msgid "Sorry, this page is not known or was moved."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/NotFound.tsx:27
|
||||
msgid "Go to the start page"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:14
|
||||
msgid "Mobile viewport detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:17
|
||||
msgid "Platform UI is optimized for Tablets and Desktops, you can use the official app for a mobile experience."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/MobileAppView.tsx:23
|
||||
msgid "Read the docs"
|
||||
msgstr ""
|
||||
|
File diff suppressed because one or more lines are too long
22
src/frontend/src/pages/Auth/Logged-In.tsx
Normal file
22
src/frontend/src/pages/Auth/Logged-In.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Text } from '@mantine/core';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { checkLoginState } from '../../functions/auth';
|
||||
|
||||
export default function Logged_In() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
checkLoginState(navigate);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>
|
||||
<Trans>Checking if you are already logged in</Trans>
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
}
|
130
src/frontend/src/pages/Auth/Login.tsx
Normal file
130
src/frontend/src/pages/Auth/Login.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Center, Container, Group, Select, Stack, Text } from '@mantine/core';
|
||||
import { useToggle } from '@mantine/hooks';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { AuthenticationForm } from '../../components/forms/AuthenticationForm';
|
||||
import { HostOptionsForm } from '../../components/forms/HostOptionsForm';
|
||||
import { EditButton } from '../../components/items/EditButton';
|
||||
import { defaultHostKey } from '../../defaults/defaultHostList';
|
||||
import { useLocalState } from '../../states/LocalState';
|
||||
import { HostList } from '../../states/states';
|
||||
|
||||
export default function Login() {
|
||||
const [hostKey, setHost, hostList] = useLocalState((state) => [
|
||||
state.hostKey,
|
||||
state.setHost,
|
||||
state.hostList
|
||||
]);
|
||||
const hostname =
|
||||
hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey].name;
|
||||
const [hostEdit, setHostEdit] = useToggle([false, true] as const);
|
||||
const hostListData = Object.keys(hostList).map((key) => ({
|
||||
value: key,
|
||||
label: hostList[key].name
|
||||
}));
|
||||
const [HostListEdit, setHostListEdit] = useToggle([false, true] as const);
|
||||
|
||||
// Data manipulation functions
|
||||
function ChangeHost(newHost: string): void {
|
||||
setHost(hostList[newHost].host, newHost);
|
||||
setHostEdit(false);
|
||||
}
|
||||
function SaveOptions(newHostList: HostList): void {
|
||||
useLocalState.setState({ hostList: newHostList });
|
||||
if (newHostList[hostKey] === undefined) {
|
||||
setHost('', '');
|
||||
}
|
||||
setHostListEdit();
|
||||
}
|
||||
// Set default host to localhost if no host is selected
|
||||
useEffect(() => {
|
||||
if (hostKey === '') {
|
||||
ChangeHost(defaultHostKey);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Center mih="100vh">
|
||||
<Container w="md" miw={400}>
|
||||
<Stack>
|
||||
<EditHostList
|
||||
hostList={hostList}
|
||||
SaveOptions={SaveOptions}
|
||||
HostListEdit={HostListEdit}
|
||||
/>
|
||||
{!HostListEdit && (
|
||||
<AuthenticationForm
|
||||
hostname={hostname}
|
||||
editing={hostEdit}
|
||||
setEditing={setHostEdit}
|
||||
selectElement={
|
||||
<SelectHost
|
||||
hostKey={hostKey}
|
||||
ChangeHost={ChangeHost}
|
||||
hostListData={hostListData}
|
||||
HostListEdit={HostListEdit}
|
||||
hostEdit={hostEdit}
|
||||
setHostListEdit={setHostListEdit}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Container>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
const SelectHost = ({
|
||||
hostKey,
|
||||
ChangeHost,
|
||||
hostListData,
|
||||
HostListEdit,
|
||||
hostEdit,
|
||||
setHostListEdit
|
||||
}: {
|
||||
hostKey: string;
|
||||
ChangeHost: (newHost: string) => void;
|
||||
hostListData: any;
|
||||
HostListEdit: boolean;
|
||||
hostEdit: boolean;
|
||||
setHostListEdit: (value?: React.SetStateAction<boolean> | undefined) => void;
|
||||
}) => {
|
||||
if (!hostEdit) return <></>;
|
||||
return (
|
||||
<Group>
|
||||
<Select
|
||||
value={hostKey}
|
||||
onChange={ChangeHost}
|
||||
data={hostListData}
|
||||
disabled={HostListEdit}
|
||||
/>
|
||||
<EditButton
|
||||
setEditing={setHostListEdit}
|
||||
editing={HostListEdit}
|
||||
disabled={HostListEdit}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const EditHostList = ({
|
||||
hostList,
|
||||
SaveOptions,
|
||||
HostListEdit
|
||||
}: {
|
||||
hostList: HostList;
|
||||
SaveOptions: (newHostList: HostList) => void;
|
||||
HostListEdit: boolean;
|
||||
}) => {
|
||||
if (!HostListEdit) return null;
|
||||
return (
|
||||
<>
|
||||
<Text>
|
||||
<Trans>Edit host options</Trans>
|
||||
</Text>
|
||||
<HostOptionsForm data={hostList} saveOptions={SaveOptions} />
|
||||
</>
|
||||
);
|
||||
};
|
48
src/frontend/src/pages/Auth/Reset.tsx
Normal file
48
src/frontend/src/pages/Auth/Reset.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Container,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { LanguageContext } from '../../contexts/LanguageContext';
|
||||
import { handleReset } from '../../functions/auth';
|
||||
|
||||
export default function Reset() {
|
||||
const simpleForm = useForm({ initialValues: { email: '' } });
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<LanguageContext>
|
||||
<Center mih="100vh">
|
||||
<Container w="md" miw={425}>
|
||||
<Stack>
|
||||
<Title>
|
||||
<Trans>Reset password</Trans>
|
||||
</Title>
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Email`}
|
||||
description={t`We will send you a link to login - if you are registered`}
|
||||
placeholder="reader@example.org"
|
||||
{...simpleForm.getInputProps('email')}
|
||||
/>
|
||||
</Stack>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={() => handleReset(navigate, simpleForm.values)}
|
||||
>
|
||||
<Trans>Send mail</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Center>
|
||||
</LanguageContext>
|
||||
);
|
||||
}
|
118
src/frontend/src/pages/Auth/Set-Password.tsx
Normal file
118
src/frontend/src/pages/Auth/Set-Password.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Container,
|
||||
PasswordInput,
|
||||
Stack,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { api } from '../../App';
|
||||
import { LanguageContext } from '../../contexts/LanguageContext';
|
||||
import { ApiPaths, url } from '../../states/ApiState';
|
||||
|
||||
export default function Set_Password() {
|
||||
const simpleForm = useForm({ initialValues: { password: '' } });
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const token = searchParams.get('token');
|
||||
const uid = searchParams.get('uid');
|
||||
|
||||
function invalidToken() {
|
||||
notifications.show({
|
||||
title: t`Token invalid`,
|
||||
message: t`You need to provide a valid token to set a new password. Check your inbox for a reset link.`,
|
||||
color: 'red'
|
||||
});
|
||||
navigate('/login');
|
||||
}
|
||||
|
||||
function passwordError(values: any) {
|
||||
notifications.show({
|
||||
title: t`Reset failed`,
|
||||
message: values?.new_password2 || values?.new_password1 || values?.token,
|
||||
color: 'red'
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// make sure we have a token
|
||||
if (!token || !uid) {
|
||||
notifications.show({
|
||||
title: t`No token provided`,
|
||||
message: t`You need to provide a token to set a new password. Check your inbox for a reset link.`,
|
||||
color: 'red'
|
||||
});
|
||||
navigate('/login');
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
function handleSet() {
|
||||
// Set password with call to backend
|
||||
api
|
||||
.post(
|
||||
url(ApiPaths.user_reset_set),
|
||||
{
|
||||
uid: uid,
|
||||
token: token,
|
||||
new_password1: simpleForm.values.password,
|
||||
new_password2: simpleForm.values.password
|
||||
},
|
||||
{ headers: { Authorization: '' } }
|
||||
)
|
||||
.then((val) => {
|
||||
if (val.status === 200) {
|
||||
notifications.show({
|
||||
title: t`Password set`,
|
||||
message: t`The password was set successfully. You can now login with your new password`,
|
||||
color: 'green',
|
||||
autoClose: false
|
||||
});
|
||||
navigate('/login');
|
||||
} else {
|
||||
passwordError(val.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (
|
||||
err.response.status === 400 &&
|
||||
err.response.data?.token == 'Invalid value'
|
||||
) {
|
||||
invalidToken();
|
||||
} else {
|
||||
passwordError(err.response.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<LanguageContext>
|
||||
<Center mih="100vh">
|
||||
<Container w="md" miw={425}>
|
||||
<Stack>
|
||||
<Title>
|
||||
<Trans>Set new password</Trans>
|
||||
</Title>
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password`}
|
||||
description={t`We will send you a link to login - if you are registered`}
|
||||
{...simpleForm.getInputProps('password')}
|
||||
/>
|
||||
</Stack>
|
||||
<Button type="submit" onClick={handleSet}>
|
||||
<Trans>Send mail</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Center>
|
||||
</LanguageContext>
|
||||
);
|
||||
}
|
36
src/frontend/src/pages/ErrorPage.tsx
Normal file
36
src/frontend/src/pages/ErrorPage.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Container, Text, Title } from '@mantine/core';
|
||||
import { useDocumentTitle } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouteError } from 'react-router-dom';
|
||||
|
||||
import { LanguageContext } from '../contexts/LanguageContext';
|
||||
import { ErrorResponse } from '../states/states';
|
||||
|
||||
export default function ErrorPage() {
|
||||
const error = useRouteError() as ErrorResponse;
|
||||
const [title, setTitle] = useState(t`Error`);
|
||||
useDocumentTitle(title);
|
||||
|
||||
useEffect(() => {
|
||||
if (error?.statusText) {
|
||||
setTitle(t`Error: ${error.statusText}`);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<LanguageContext>
|
||||
<Container>
|
||||
<Title>
|
||||
<Trans>Error</Trans>
|
||||
</Title>
|
||||
<Text>
|
||||
<Trans>Sorry, an unexpected error has occurred.</Trans>
|
||||
</Text>
|
||||
<Text>
|
||||
<i>{error.statusText || error.message}</i>
|
||||
</Text>
|
||||
</Container>
|
||||
</LanguageContext>
|
||||
);
|
||||
}
|
18
src/frontend/src/pages/Index/Home.tsx
Normal file
18
src/frontend/src/pages/Index/Home.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Group } from '@mantine/core';
|
||||
|
||||
import { PlaceholderPill } from '../../components/items/Placeholder';
|
||||
import { StylishText } from '../../components/items/StylishText';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Group>
|
||||
<StylishText>
|
||||
<Trans>Home</Trans>
|
||||
</StylishText>
|
||||
<PlaceholderPill />
|
||||
</Group>
|
||||
</>
|
||||
);
|
||||
}
|
33
src/frontend/src/pages/Index/Profile/Profile.tsx
Normal file
33
src/frontend/src/pages/Index/Profile/Profile.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Tabs } from '@mantine/core';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { StylishText } from '../../../components/items/StylishText';
|
||||
import { UserPanel } from './UserPanel';
|
||||
|
||||
export default function Profile() {
|
||||
const navigate = useNavigate();
|
||||
const { tabValue } = useParams();
|
||||
|
||||
return (
|
||||
<>
|
||||
<StylishText>
|
||||
<Trans>Profile</Trans>
|
||||
</StylishText>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
onTabChange={(value) => navigate(`/profile/${value}`)}
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="user">
|
||||
<Trans>User</Trans>
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="user">
|
||||
<UserPanel />
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
124
src/frontend/src/pages/Index/Profile/UserPanel.tsx
Normal file
124
src/frontend/src/pages/Index/Profile/UserPanel.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
Grid,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useToggle } from '@mantine/hooks';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api, queryClient } from '../../../App';
|
||||
import { EditButton } from '../../../components/items/EditButton';
|
||||
import { UserTheme } from './UserTheme';
|
||||
|
||||
export function UserPanel() {
|
||||
// view
|
||||
const PRIMARY_COL_HEIGHT = 300;
|
||||
const SECONDARY_COL_HEIGHT = PRIMARY_COL_HEIGHT / 2 - 8;
|
||||
|
||||
// data
|
||||
function fetchData() {
|
||||
return api.get('user/me/').then((res) => res.data);
|
||||
}
|
||||
const { isLoading, data } = useQuery({
|
||||
queryKey: ['user-me'],
|
||||
queryFn: fetchData
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SimpleGrid cols={2} spacing="md">
|
||||
<Container w="100%">
|
||||
{isLoading ? (
|
||||
<Skeleton height={SECONDARY_COL_HEIGHT} />
|
||||
) : (
|
||||
<UserInfo data={data} />
|
||||
)}
|
||||
</Container>
|
||||
<Grid gutter="md">
|
||||
<Grid.Col>
|
||||
<UserTheme height={SECONDARY_COL_HEIGHT} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<Skeleton height={SECONDARY_COL_HEIGHT} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<Skeleton height={SECONDARY_COL_HEIGHT} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</SimpleGrid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UserInfo({ data }: { data: any }) {
|
||||
if (!data) return <Skeleton />;
|
||||
|
||||
const form = useForm({ initialValues: data });
|
||||
const [editing, setEditing] = useToggle([false, true] as const);
|
||||
function SaveData(values: any) {
|
||||
api.put('user/me/', values).then((res) => {
|
||||
if (res.status === 200) {
|
||||
setEditing();
|
||||
queryClient.invalidateQueries(['user-me']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit((values) => SaveData(values))}>
|
||||
<Group>
|
||||
<Title order={3}>
|
||||
<Trans>Userinfo</Trans>
|
||||
</Title>
|
||||
<EditButton setEditing={setEditing} editing={editing} />
|
||||
</Group>
|
||||
<Group>
|
||||
{editing ? (
|
||||
<Stack spacing="xs">
|
||||
<TextInput
|
||||
label="First name"
|
||||
placeholder="First name"
|
||||
{...form.getInputProps('first_name')}
|
||||
/>
|
||||
<TextInput
|
||||
label="Last name"
|
||||
placeholder="Last name"
|
||||
{...form.getInputProps('last_name')}
|
||||
/>
|
||||
<TextInput
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
{...form.getInputProps('username')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button type="submit">
|
||||
<Trans>Submit</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack spacing="xs">
|
||||
<Text>
|
||||
<Trans>First name: {form.values.first_name}</Trans>
|
||||
</Text>
|
||||
<Text>
|
||||
<Trans>Last name: {form.values.last_name}</Trans>
|
||||
</Text>
|
||||
<Text>
|
||||
<Trans>Username: {form.values.username}</Trans>
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Group>
|
||||
</form>
|
||||
);
|
||||
}
|
154
src/frontend/src/pages/Index/Profile/UserTheme.tsx
Normal file
154
src/frontend/src/pages/Index/Profile/UserTheme.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
ColorInput,
|
||||
ColorPicker,
|
||||
Container,
|
||||
DEFAULT_THEME,
|
||||
Group,
|
||||
Loader,
|
||||
Select,
|
||||
Slider,
|
||||
Space,
|
||||
Table,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { LoaderType } from '@mantine/styles/lib/theme/types/MantineTheme';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { PlaceholderPill } from '../../../components/items/Placeholder';
|
||||
import { SizeMarks } from '../../../defaults/defaults';
|
||||
import { InvenTreeStyle } from '../../../globalStyle';
|
||||
import { useLocalState } from '../../../states/LocalState';
|
||||
|
||||
function getLkp(color: string) {
|
||||
return { [DEFAULT_THEME.colors[color][6]]: color };
|
||||
}
|
||||
const LOOKUP = Object.assign(
|
||||
{},
|
||||
...Object.keys(DEFAULT_THEME.colors).map((clr) => getLkp(clr))
|
||||
);
|
||||
|
||||
export function UserTheme({ height }: { height: number }) {
|
||||
const { theme } = InvenTreeStyle();
|
||||
|
||||
// primary color
|
||||
function changePrimary(color: string) {
|
||||
useLocalState.setState({ primaryColor: LOOKUP[color] });
|
||||
}
|
||||
// white color
|
||||
const [whiteColor, setWhiteColor] = useState(theme.white);
|
||||
function changeWhite(color: string) {
|
||||
useLocalState.setState({ whiteColor: color });
|
||||
setWhiteColor(color);
|
||||
}
|
||||
// black color
|
||||
const [blackColor, setBlackColor] = useState(theme.black);
|
||||
function changeBlack(color: string) {
|
||||
useLocalState.setState({ blackColor: color });
|
||||
setBlackColor(color);
|
||||
}
|
||||
// radius
|
||||
function getMark(value: number) {
|
||||
const obj = SizeMarks.find((mark) => mark.value === value);
|
||||
if (obj) return obj;
|
||||
return SizeMarks[0];
|
||||
}
|
||||
function getDefaultRadius() {
|
||||
const obj = SizeMarks.find(
|
||||
(mark) => mark.label === useLocalState.getState().radius
|
||||
);
|
||||
if (obj) return obj.value;
|
||||
return 50;
|
||||
}
|
||||
const [radius, setRadius] = useState(getDefaultRadius());
|
||||
function changeRadius(value: number) {
|
||||
setRadius(value);
|
||||
useLocalState.setState({ radius: getMark(value).label });
|
||||
}
|
||||
// loader
|
||||
const loaderDate = [
|
||||
{ value: 'bars', label: t`bars` },
|
||||
{ value: 'oval', label: t`oval` },
|
||||
{ value: 'dots', label: t`dots` }
|
||||
];
|
||||
const [loader, setLoader] = useState<LoaderType>(theme.loader);
|
||||
function changeLoader(value: LoaderType) {
|
||||
setLoader(value);
|
||||
useLocalState.setState({ loader: value });
|
||||
}
|
||||
|
||||
return (
|
||||
<Container w="100%" mih={height} p={0}>
|
||||
<Title order={3}>
|
||||
<Trans>
|
||||
Design <PlaceholderPill />
|
||||
</Trans>
|
||||
</Title>
|
||||
<Table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>Primary color</Trans>
|
||||
</td>
|
||||
<td>
|
||||
<ColorPicker
|
||||
format="hex"
|
||||
onChange={changePrimary}
|
||||
withPicker={false}
|
||||
swatches={Object.keys(LOOKUP)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>White color</Trans>
|
||||
</td>
|
||||
<td>
|
||||
<ColorInput value={whiteColor} onChange={changeWhite} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>Black color</Trans>
|
||||
</td>
|
||||
<td>
|
||||
<ColorInput value={blackColor} onChange={changeBlack} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>Border Radius</Trans>
|
||||
</td>
|
||||
<td>
|
||||
<Slider
|
||||
label={(val) => getMark(val).label}
|
||||
defaultValue={50}
|
||||
step={25}
|
||||
marks={SizeMarks}
|
||||
value={radius}
|
||||
onChange={changeRadius}
|
||||
mb={18}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>Loader</Trans>
|
||||
</td>
|
||||
<td>
|
||||
<Group align="center">
|
||||
<Loader type={loader} mah={18} />
|
||||
<Space w={10} />
|
||||
<Select
|
||||
data={loaderDate}
|
||||
value={loader}
|
||||
onChange={changeLoader}
|
||||
/>
|
||||
</Group>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Container>
|
||||
);
|
||||
}
|
35
src/frontend/src/pages/NotFound.tsx
Normal file
35
src/frontend/src/pages/NotFound.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Button, Center, Container, Stack, Text, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { LanguageContext } from '../contexts/LanguageContext';
|
||||
|
||||
export default function NotFound() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<LanguageContext>
|
||||
<Center mih="100vh">
|
||||
<Container w="md" miw={400}>
|
||||
<Stack>
|
||||
<Title>
|
||||
<Trans>Not Found</Trans>
|
||||
</Title>
|
||||
<Text>
|
||||
<Trans>Sorry, this page is not known or was moved.</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="green"
|
||||
onClick={() => navigate('/')}
|
||||
>
|
||||
<Trans>Go to the start page</Trans>
|
||||
<IconArrowBack />
|
||||
</Button>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Center>
|
||||
</LanguageContext>
|
||||
);
|
||||
}
|
72
src/frontend/src/router.tsx
Normal file
72
src/frontend/src/router.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { lazy } from 'react';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
|
||||
import { Loadable } from './functions/loading';
|
||||
|
||||
// Lazy loaded pages
|
||||
export const LayoutComponent = Loadable(
|
||||
lazy(() => import('./components/nav/Layout'))
|
||||
);
|
||||
export const Home = Loadable(lazy(() => import('./pages/Index/Home')));
|
||||
export const ErrorPage = Loadable(lazy(() => import('./pages/ErrorPage')));
|
||||
export const Profile = Loadable(
|
||||
lazy(() => import('./pages/Index/Profile/Profile'))
|
||||
);
|
||||
export const NotFound = Loadable(lazy(() => import('./pages/NotFound')));
|
||||
export const Login = Loadable(lazy(() => import('./pages/Auth/Login')));
|
||||
export const Logged_In = Loadable(lazy(() => import('./pages/Auth/Logged-In')));
|
||||
export const Reset = Loadable(lazy(() => import('./pages/Auth/Reset')));
|
||||
export const Set_Password = Loadable(
|
||||
lazy(() => import('./pages/Auth/Set-Password'))
|
||||
);
|
||||
|
||||
// Routes
|
||||
export const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFound />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
element: <LayoutComponent />,
|
||||
errorElement: <ErrorPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />
|
||||
},
|
||||
{
|
||||
path: 'home/',
|
||||
element: <Home />
|
||||
},
|
||||
{
|
||||
path: '/profile/:tabValue',
|
||||
element: <Profile />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
element: <Login />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/logged-in',
|
||||
element: <Logged_In />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/reset-password',
|
||||
element: <Reset />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/set-password',
|
||||
element: <Set_Password />,
|
||||
errorElement: <ErrorPage />
|
||||
}
|
||||
],
|
||||
{ basename: '/platform' }
|
||||
);
|
61
src/frontend/src/states/ApiState.tsx
Normal file
61
src/frontend/src/states/ApiState.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { api } from '../App';
|
||||
import { emptyServerAPI } from '../defaults/defaults';
|
||||
import { ServerAPIProps, UserProps } from './states';
|
||||
|
||||
interface ApiStateProps {
|
||||
user: UserProps | undefined;
|
||||
setUser: (newUser: UserProps) => void;
|
||||
server: ServerAPIProps;
|
||||
setServer: (newServer: ServerAPIProps) => void;
|
||||
fetchApiState: () => void;
|
||||
}
|
||||
|
||||
export const useApiState = create<ApiStateProps>((set, get) => ({
|
||||
user: undefined,
|
||||
setUser: (newUser: UserProps) => set({ user: newUser }),
|
||||
server: emptyServerAPI,
|
||||
setServer: (newServer: ServerAPIProps) => set({ server: newServer }),
|
||||
fetchApiState: async () => {
|
||||
// Fetch user data
|
||||
await api.get(url(ApiPaths.user_me)).then((response) => {
|
||||
const user: UserProps = {
|
||||
name: `${response.data.first_name} ${response.data.last_name}`,
|
||||
email: response.data.email,
|
||||
username: response.data.username
|
||||
};
|
||||
set({ user: user });
|
||||
});
|
||||
// Fetch server data
|
||||
await api.get('/').then((response) => {
|
||||
set({ server: response.data });
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
export enum ApiPaths {
|
||||
user_me = 'api-user-me',
|
||||
user_token = 'api-user-token',
|
||||
user_simple_login = 'api-user-simple-login',
|
||||
user_reset = 'api-user-reset',
|
||||
user_reset_set = 'api-user-reset-set'
|
||||
}
|
||||
|
||||
export function url(path: ApiPaths, pk?: any): string {
|
||||
switch (path) {
|
||||
case ApiPaths.user_me:
|
||||
return 'user/me/';
|
||||
case ApiPaths.user_token:
|
||||
return 'user/token/';
|
||||
case ApiPaths.user_simple_login:
|
||||
return 'email/generate/';
|
||||
case ApiPaths.user_reset:
|
||||
return '/auth/password/reset/';
|
||||
case ApiPaths.user_reset_set:
|
||||
return '/auth/password/reset/confirm/';
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
48
src/frontend/src/states/LocalState.tsx
Normal file
48
src/frontend/src/states/LocalState.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { MantineNumberSize } from '@mantine/core';
|
||||
import { LoaderType } from '@mantine/styles/lib/theme/types/MantineTheme';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
import { Locales } from '../contexts/LanguageContext';
|
||||
import { HostList } from './states';
|
||||
|
||||
interface LocalStateProps {
|
||||
autoupdate: boolean;
|
||||
toggleAutoupdate: () => void;
|
||||
host: string;
|
||||
setHost: (newHost: string, newHostKey: string) => void;
|
||||
hostKey: string;
|
||||
hostList: HostList;
|
||||
language: Locales;
|
||||
// theme
|
||||
primaryColor: string;
|
||||
whiteColor: string;
|
||||
blackColor: string;
|
||||
radius: MantineNumberSize;
|
||||
loader: LoaderType;
|
||||
}
|
||||
|
||||
export const useLocalState = create<LocalStateProps>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
autoupdate: false,
|
||||
toggleAutoupdate: () =>
|
||||
set((state) => ({ autoupdate: !state.autoupdate })),
|
||||
host: '',
|
||||
setHost: (newHost, newHostKey) =>
|
||||
set({ host: newHost, hostKey: newHostKey }),
|
||||
hostKey: '',
|
||||
hostList: {},
|
||||
language: 'en',
|
||||
//theme
|
||||
primaryColor: 'indigo',
|
||||
whiteColor: '#fff',
|
||||
blackColor: '#000',
|
||||
radius: 'md',
|
||||
loader: 'oval'
|
||||
}),
|
||||
{
|
||||
name: 'session-settings'
|
||||
}
|
||||
)
|
||||
);
|
25
src/frontend/src/states/SessionState.tsx
Normal file
25
src/frontend/src/states/SessionState.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
import { setApiDefaults } from '../App';
|
||||
|
||||
interface SessionStateProps {
|
||||
token: string | undefined;
|
||||
setToken: (newToken: string | undefined) => void;
|
||||
}
|
||||
|
||||
export const useSessionState = create<SessionStateProps>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
token: '',
|
||||
setToken: (newToken) => {
|
||||
set({ token: newToken });
|
||||
setApiDefaults();
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: 'session-state',
|
||||
getStorage: () => sessionStorage
|
||||
}
|
||||
)
|
||||
);
|
39
src/frontend/src/states/states.tsx
Normal file
39
src/frontend/src/states/states.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
export interface Host {
|
||||
host: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface HostList {
|
||||
[key: string]: Host;
|
||||
}
|
||||
|
||||
export interface UserProps {
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface ServerAPIProps {
|
||||
server: null | string;
|
||||
version: null | string;
|
||||
instance: null | string;
|
||||
apiVersion: null | number;
|
||||
worker_running: null | boolean;
|
||||
worker_pending_tasks: null | number;
|
||||
plugins_enabled: null | boolean;
|
||||
active_plugins: PluginProps[];
|
||||
}
|
||||
|
||||
export interface PluginProps {
|
||||
name: string;
|
||||
slug: string;
|
||||
version: null | string;
|
||||
}
|
||||
|
||||
// Errors
|
||||
export type ErrorResponse = {
|
||||
data: any;
|
||||
status: number;
|
||||
statusText: string;
|
||||
message?: string;
|
||||
};
|
42
src/frontend/src/views/DesktopAppView.tsx
Normal file
42
src/frontend/src/views/DesktopAppView.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
|
||||
import { queryClient, setApiDefaults } from '../App';
|
||||
import { BaseContext } from '../contexts/BaseContext';
|
||||
import { defaultHostList } from '../defaults/defaultHostList';
|
||||
import { router } from '../router';
|
||||
import { useApiState } from '../states/ApiState';
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
import { useSessionState } from '../states/SessionState';
|
||||
|
||||
export default function DesktopAppView() {
|
||||
const [hostList] = useLocalState((state) => [state.hostList]);
|
||||
const [fetchApiState] = useApiState((state) => [state.fetchApiState]);
|
||||
|
||||
// Local state initialization
|
||||
if (Object.keys(hostList).length === 0) {
|
||||
console.log('Loading default host list');
|
||||
useLocalState.setState({ hostList: defaultHostList });
|
||||
}
|
||||
setApiDefaults();
|
||||
|
||||
// Server Session
|
||||
const [fetchedServerSession, setFetchedServerSession] = useState(false);
|
||||
const sessionState = useSessionState.getState();
|
||||
const [token] = sessionState.token ? [sessionState.token] : [null];
|
||||
useEffect(() => {
|
||||
if (token && !fetchedServerSession) {
|
||||
setFetchedServerSession(true);
|
||||
fetchApiState();
|
||||
}
|
||||
}, [token, fetchedServerSession]);
|
||||
|
||||
return (
|
||||
<BaseContext>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</BaseContext>
|
||||
);
|
||||
}
|
30
src/frontend/src/views/MobileAppView.tsx
Normal file
30
src/frontend/src/views/MobileAppView.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Anchor, Center, Container, Stack, Text, Title } from '@mantine/core';
|
||||
|
||||
import { BaseContext } from '../contexts/BaseContext';
|
||||
import { docLinks } from '../defaults/links';
|
||||
|
||||
export default function MobileAppView() {
|
||||
return (
|
||||
<BaseContext>
|
||||
<Center h="100vh">
|
||||
<Container>
|
||||
<Stack>
|
||||
<Title color="red">
|
||||
<Trans>Mobile viewport detected</Trans>
|
||||
</Title>
|
||||
<Text>
|
||||
<Trans>
|
||||
Platform UI is optimized for Tablets and Desktops, you can use
|
||||
the official app for a mobile experience.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Anchor href={docLinks.app}>
|
||||
<Trans>Read the docs</Trans>
|
||||
</Anchor>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Center>
|
||||
</BaseContext>
|
||||
);
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import exp from 'constants';
|
||||
|
||||
test('Check classic index site', async ({ page }) => {
|
||||
await page.goto('./api/');
|
||||
|
@ -1,20 +1,15 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test('Basic Platform UI test', async ({ page }) => {
|
||||
await page.goto('./index/');
|
||||
await expect(page).toHaveTitle('InvenTree Demo Server | Sign In');
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'InvenTree Demo Server' })
|
||||
).toBeVisible();
|
||||
|
||||
await page.goto('./platform/');
|
||||
await expect(page).toHaveTitle('InvenTree Demo Server');
|
||||
await page.waitForURL('**/platform/');
|
||||
await page.getByLabel('username').fill('allaccess');
|
||||
await page.getByLabel('password').fill('nolimits');
|
||||
await page.click('button', { text: 'Sign In' });
|
||||
|
||||
await page.getByRole('button', { name: 'Log in' }).click();
|
||||
await page.waitForURL('**/platform/home');
|
||||
await page.goto('./platform/');
|
||||
|
||||
await expect(page).toHaveTitle('InvenTree Demo Server');
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Welcome to the new frontend!' })
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('Home').nth(1)).toBeVisible();
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig, splitVendorChunkPlugin } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
@ -8,7 +8,8 @@ export default defineConfig({
|
||||
babel: {
|
||||
plugins: ['macros']
|
||||
}
|
||||
})
|
||||
}),
|
||||
splitVendorChunkPlugin()
|
||||
],
|
||||
build: {
|
||||
manifest: true,
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user