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:
Matthias Mair 2023-07-21 00:54:57 +02:00 committed by GitHub
parent 9f96620e12
commit 1ab772e66d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 6817 additions and 78 deletions

View File

@ -52,7 +52,8 @@ repos:
(?x)^( (?x)^(
docs/docs/stylesheets/.*| docs/docs/stylesheets/.*|
docs/docs/javascripts/.*| docs/docs/javascripts/.*|
docs/docs/webfonts/.* docs/docs/webfonts/.* |
src/frontend/src/locales/.* |
)$ )$
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: "v3.0.0-alpha.9-for-vscode" rev: "v3.0.0-alpha.9-for-vscode"

View File

@ -33,6 +33,7 @@ from report.api import report_api_urls
from stock.api import stock_api_urls from stock.api import stock_api_urls
from stock.urls import stock_urls from stock.urls import stock_urls
from users.api import user_urls from users.api import user_urls
from web.urls import spa_view
from web.urls import urlpatterns as platform_urls from web.urls import urlpatterns as platform_urls
from .api import APISearchView, InfoView, NotFoundView from .api import APISearchView, InfoView, NotFoundView
@ -210,7 +211,7 @@ classic_frontendpatterns = [
new_frontendpatterns = [ new_frontendpatterns = [
# Platform urls # Platform urls
re_path(r'^platform/', include(platform_urls)), re_path(r'^platform/', include(platform_urls)),
re_path(r'^platform', spa_view, name='platform'),
] ]
# Load patterns for frontend according to settings # Load patterns for frontend according to settings

View File

@ -22,5 +22,6 @@ spa_view = ensure_csrf_cookie(TemplateView.as_view(template_name="web/index.html
urlpatterns = [ urlpatterns = [
path("assets/<path:path>", RedirectAssetView.as_view()), path("assets/<path:path>", RedirectAssetView.as_view()),
re_path(r"^(?P<path>.*)/$", spa_view), re_path(r"^(?P<path>.*)/$", spa_view),
path("set-password?uid=<uid>&token=<token>", spa_view, name="password_reset_confirm"),
path("", spa_view), path("", spa_view),
] ]

View File

@ -12,8 +12,5 @@
"fallbackLocales": { "fallbackLocales": {
"default": "en", "default": "en",
"pseudo-LOCALE": "en" "pseudo-LOCALE": "en"
},
"extractBabelOptions": {
"presets": ["@babel/preset-typescript"]
} }
} }

View File

@ -3,7 +3,6 @@
<head> <head>
<meta charset="UTF-8" /> <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" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvenTree</title> <title>InvenTree</title>
</head> </head>

View File

@ -11,10 +11,29 @@
"compile": "lingui compile --typescript" "compile": "lingui compile --typescript"
}, },
"dependencies": { "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/core": "^4.2.1",
"@lingui/react": "^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": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0",
"react-router-dom": "^6.4.3",
"zustand": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.5", "@babel/core": "^7.20.5",

View File

@ -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 // Main App
export default function App() { export default function App() {
// Check if mobile
if (checkMobile()) {
const MobileAppView = Loadable(lazy(() => import('./views/MobileAppView')));
return <MobileAppView />;
}
// Main App component // Main App component
return ( const DesktopAppView = Loadable(lazy(() => import('./views/DesktopAppView')));
<> return <DesktopAppView />;
<h1>Welcome to the new frontend!</h1>
<p>This is a placeholder site</p>
</>
);
} }

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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}
</>
);
}

View 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>
);
});

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
};

View 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;
}

View 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>
);
}

View 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';

View 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' }
];

View 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
}
];

View 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>
}
];

View 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');
});
}

View 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>
);

View 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
}
}));

View File

@ -12,3 +12,794 @@ msgstr ""
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \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émons cry is very loud and distracting"
#~ msgstr ""
#: src/defaults/menuItems.tsx:12
#~ msgid "This Pokémons 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 Smeargles tail secretions changes"
#~ msgstr ""
#: src/defaults/menuItems.tsx:24
#~ msgid "The fluid of Smeargles 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 shells 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

View File

@ -12,3 +12,794 @@ msgstr ""
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \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émons cry is very loud and distracting"
#~ msgstr "This Pokémons cry is very loud and distracting"
#: src/defaults/menuItems.tsx:12
#~ msgid "This Pokémons cry is very loud and distracting and more and more and more"
#~ msgstr "This Pokémons 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 Smeargles tail secretions changes"
#~ msgstr "The fluid of Smeargles tail secretions changes"
#: src/defaults/menuItems.tsx:24
#~ msgid "The fluid of Smeargles tail secretions changes in the intensity"
#~ msgstr "The fluid of Smeargles 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 shells rounded shape and the grooves on its."
#~ msgstr "The shells 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

View File

@ -12,3 +12,838 @@ msgstr ""
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \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émons cry is very loud and distracting"
#~ msgstr ""
#: src/defaults/menuItems.tsx:12
#~ msgid "This Pokémons 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 Smeargles tail secretions changes"
#~ msgstr ""
#: src/defaults/menuItems.tsx:24
#~ msgid "The fluid of Smeargles 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 shells 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

View File

@ -12,3 +12,838 @@ msgstr ""
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \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émons cry is very loud and distracting"
#~ msgstr ""
#: src/defaults/menuItems.tsx:12
#~ msgid "This Pokémons 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 Smeargles tail secretions changes"
#~ msgstr ""
#: src/defaults/menuItems.tsx:24
#~ msgid "The fluid of Smeargles 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 shells 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

View 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>
</>
);
}

View 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} />
</>
);
};

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
</>
);
}

View 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>
</>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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' }
);

View 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 '';
}
}

View 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'
}
)
);

View 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
}
)
);

View 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;
};

View 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>
);
}

View 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>
);
}

View File

@ -1,5 +1,4 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import exp from 'constants';
test('Check classic index site', async ({ page }) => { test('Check classic index site', async ({ page }) => {
await page.goto('./api/'); await page.goto('./api/');

View File

@ -1,20 +1,15 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
test('Basic Platform UI test', async ({ page }) => { test('Basic Platform UI test', async ({ page }) => {
await page.goto('./index/'); await page.goto('./platform/');
await expect(page).toHaveTitle('InvenTree Demo Server | Sign In'); await expect(page).toHaveTitle('InvenTree Demo Server');
await expect( await page.waitForURL('**/platform/');
page.getByRole('heading', { name: 'InvenTree Demo Server' })
).toBeVisible();
await page.getByLabel('username').fill('allaccess'); await page.getByLabel('username').fill('allaccess');
await page.getByLabel('password').fill('nolimits'); 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 page.goto('./platform/');
await expect(page).toHaveTitle('InvenTree Demo Server'); await expect(page).toHaveTitle('InvenTree Demo Server');
await expect( await expect(page.getByText('Home').nth(1)).toBeVisible();
page.getByRole('heading', { name: 'Welcome to the new frontend!' })
).toBeVisible();
}); });

View File

@ -1,5 +1,5 @@
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite'; import { defineConfig, splitVendorChunkPlugin } from 'vite';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@ -8,7 +8,8 @@ export default defineConfig({
babel: { babel: {
plugins: ['macros'] plugins: ['macros']
} }
}) }),
splitVendorChunkPlugin()
], ],
build: { build: {
manifest: true, manifest: true,

File diff suppressed because it is too large Load Diff