2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 04:25:42 +00:00

feat: Re-Implement customize options (#8969)

* Extend api to also include customize functions

* [FR] Re-Implement customize options
Fixes #8818

* re-implement header

* add splashscreen customisation

* make simpler

* fix rendering

* bump api
This commit is contained in:
Matthias Mair
2025-01-31 03:10:31 +01:00
committed by GitHub
parent e75ceb0719
commit cfa248aad9
7 changed files with 135 additions and 35 deletions

View File

@ -1,4 +1,11 @@
import { ActionIcon, Container, Group, Indicator, Tabs } from '@mantine/core';
import {
ActionIcon,
Container,
Group,
Indicator,
Tabs,
Text
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconBell, IconSearch } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
@ -10,7 +17,7 @@ import { navTabs as mainNavTabs } from '../../defaults/links';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { navigateToLink } from '../../functions/navigation';
import * as classes from '../../main.css';
import { apiUrl } from '../../states/ApiState';
import { apiUrl, useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
import { useGlobalSettingsState } from '../../states/SettingsState';
import { useUserState } from '../../states/UserState';
@ -27,6 +34,7 @@ export function Header() {
state.setNavigationOpen,
state.navigationOpen
]);
const [server] = useServerApiState((state) => [state.server]);
const [navDrawerOpened, { open: openNavDrawer, close: closeNavDrawer }] =
useDisclosure(navigationOpen);
const [
@ -40,11 +48,13 @@ export function Header() {
] = useDisclosure(false);
const { isLoggedIn } = useUserState();
const [notificationCount, setNotificationCount] = useState<number>(0);
const globalSettings = useGlobalSettingsState();
const navbar_message = useMemo(() => {
return server.customize?.navbar_message;
}, [server.customize]);
// Fetch number of notifications for the current user
const notifications = useQuery({
queryKey: ['notification-count'],
@ -105,6 +115,12 @@ export function Header() {
<NavHoverMenu openDrawer={openNavDrawer} />
<NavTabs />
</Group>
{navbar_message && (
<Text>
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation> */}
<div dangerouslySetInnerHTML={{ __html: navbar_message }} />
</Text>
)}
<Group>
<ActionIcon
onClick={openSearchDrawer}

View File

@ -19,7 +19,8 @@ export const emptyServerAPI = {
installer: null,
target: null,
default_locale: null,
django_admin: null
django_admin: null,
customize: null
};
export interface SiteMarkProps {

View File

@ -1,9 +1,15 @@
import { Trans, t } from '@lingui/macro';
import { Center, Container, Paper, Text } from '@mantine/core';
import {
BackgroundImage,
Center,
Container,
Divider,
Paper,
Text
} from '@mantine/core';
import { useDisclosure, useToggle } from '@mantine/hooks';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { setApiDefaults } from '../../App';
import { AuthFormOptions } from '../../components/forms/AuthFormOptions';
import {
@ -18,6 +24,7 @@ import {
doBasicLogin,
followRedirect
} from '../../functions/auth';
import { generateUrl } from '../../functions/urls';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
@ -39,6 +46,34 @@ export default function Login() {
const location = useLocation();
const [searchParams] = useSearchParams();
const LoginMessage = useMemo(() => {
const val = server.customize?.login_message;
if (val) {
return (
<>
<Divider my='md' />
<Text>
<span
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{ __html: val }}
/>
</Text>
</>
);
}
return null;
}, [server.customize]);
const SplashComponent = useMemo(() => {
const temp = server.customize?.splash;
if (temp) {
return ({ children }: { children: React.ReactNode }) => (
<BackgroundImage src={generateUrl(temp)}>{children}</BackgroundImage>
);
}
return ({ children }: { children: React.ReactNode }) => <>{children}</>;
}, [server.customize]);
// Data manipulation functions
function ChangeHost(newHost: string | null): void {
if (newHost === null) return;
@ -75,31 +110,45 @@ export default function Login() {
// Main rendering block
return (
<Center mih='100vh'>
<Container w='md' miw={400}>
{hostEdit ? (
<InstanceOptions
hostKey={hostKey}
ChangeHost={ChangeHost}
setHostEdit={setHostEdit}
/>
) : (
<>
<Paper radius='md' p='xl' withBorder>
<Text size='lg' fw={500}>
{loginMode ? (
<Trans>Welcome, log in below</Trans>
) : (
<Trans>Register below</Trans>
)}
</Text>
{loginMode ? <AuthenticationForm /> : <RegistrationForm />}
<ModeSelector loginMode={loginMode} setMode={setMode} />
</Paper>
<AuthFormOptions hostname={hostname} toggleHostEdit={setHostEdit} />
</>
)}
</Container>
</Center>
<SplashComponent>
<Center mih='100vh'>
<div
style={{
padding: '10px',
backgroundColor: 'rgba(0,0,0,0.5)',
boxShadow: '0 0 15px 10px rgba(0,0,0,0.5)'
}}
>
<Container w='md' miw={400}>
{hostEdit ? (
<InstanceOptions
hostKey={hostKey}
ChangeHost={ChangeHost}
setHostEdit={setHostEdit}
/>
) : (
<>
<Paper radius='md' p='xl' withBorder>
<Text size='lg' fw={500}>
{loginMode ? (
<Trans>Welcome, log in below</Trans>
) : (
<Trans>Register below</Trans>
)}
</Text>
{loginMode ? <AuthenticationForm /> : <RegistrationForm />}
<ModeSelector loginMode={loginMode} setMode={setMode} />
{LoginMessage}
</Paper>
<AuthFormOptions
hostname={hostname}
toggleHostEdit={setHostEdit}
/>
</>
)}
</Container>
</div>
</Center>
</SplashComponent>
);
}

View File

@ -48,6 +48,12 @@ export interface ServerAPIProps {
target: null | string;
default_locale: null | string;
django_admin: null | string;
customize: null | {
logo: string;
splash: string;
login_message: string;
navbar_message: string;
};
}
export interface AuthProps {