mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-16 01:08:12 +00:00
feat(frontend): Add start page with quick actions to Admin Center (#7995)
* add option to set leftMargin * Add home tab and action button * make home button actually go to home * Add general info text * Add dependeant quick action section * Add Quickaction to home page * use Carousel * style check * small fixes * add permanent alerts to Admin Center Home * also show inactive alerts * fix order of alerts * simplify attrs * remove security section for now * bring quick actions alive * adjust text * Use StylishText * Make alert columns reactive * Adjust text formatting * Refactor <QuickActions /> - Use responsive grid instead of carousel - Add icons - Translate text --------- Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
This commit is contained in:
@@ -2,12 +2,14 @@ import { ActionIcon, Alert, Group, Menu, Stack, Tooltip } from '@mantine/core';
|
|||||||
import { IconExclamationCircle } from '@tabler/icons-react';
|
import { IconExclamationCircle } from '@tabler/icons-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import type { SettingsStateProps } from '@lib/types/Settings';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { docLinks } from '../../defaults/links';
|
import { docLinks } from '../../defaults/links';
|
||||||
import { useServerApiState } from '../../states/ServerApiState';
|
import { useServerApiState } from '../../states/ServerApiState';
|
||||||
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import type { ServerAPIProps } from '../../states/states';
|
||||||
|
|
||||||
interface AlertInfo {
|
interface AlertInfo {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -32,64 +34,21 @@ export function Alerts() {
|
|||||||
|
|
||||||
const [dismissed, setDismissed] = useState<string[]>([]);
|
const [dismissed, setDismissed] = useState<string[]>([]);
|
||||||
|
|
||||||
const alerts: AlertInfo[] = useMemo(() => {
|
const alerts: AlertInfo[] = useMemo(
|
||||||
const _alerts: AlertInfo[] = [];
|
() =>
|
||||||
|
getAlerts(server, globalSettings).filter(
|
||||||
if (server?.debug_mode) {
|
(alert) => !dismissed.includes(alert.key)
|
||||||
_alerts.push({
|
),
|
||||||
key: 'debug',
|
[server, dismissed, globalSettings]
|
||||||
title: t`Debug Mode`,
|
);
|
||||||
code: 'INVE-W4',
|
|
||||||
message: t`The server is running in debug mode.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!server?.worker_running) {
|
|
||||||
_alerts.push({
|
|
||||||
key: 'worker',
|
|
||||||
title: t`Background Worker`,
|
|
||||||
code: 'INVE-W5',
|
|
||||||
message: t`The background worker process is not running.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!server?.email_configured) {
|
|
||||||
_alerts.push({
|
|
||||||
key: 'email',
|
|
||||||
title: t`Email settings`,
|
|
||||||
code: 'INVE-W7',
|
|
||||||
message: t`Email settings not configured.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (globalSettings.isSet('SERVER_RESTART_REQUIRED')) {
|
|
||||||
_alerts.push({
|
|
||||||
key: 'restart',
|
|
||||||
title: t`Server Restart`,
|
|
||||||
code: 'INVE-W6',
|
|
||||||
message: t`The server requires a restart to apply changes.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const n_migrations =
|
|
||||||
Number.parseInt(globalSettings.getSetting('_PENDING_MIGRATIONS')) ?? 0;
|
|
||||||
|
|
||||||
if (n_migrations > 0) {
|
|
||||||
_alerts.push({
|
|
||||||
key: 'migrations',
|
|
||||||
title: t`Database Migrations`,
|
|
||||||
code: 'INVE-W8',
|
|
||||||
message: t`There are pending database migrations.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return _alerts.filter((alert) => !dismissed.includes(alert.key));
|
|
||||||
}, [server, dismissed, globalSettings]);
|
|
||||||
|
|
||||||
const anyErrors: boolean = useMemo(
|
const anyErrors: boolean = useMemo(
|
||||||
() => alerts.some((alert) => alert.error),
|
() => alerts.some((alert) => alert.error),
|
||||||
[alerts]
|
[alerts]
|
||||||
);
|
);
|
||||||
|
function closeAlert(key: string) {
|
||||||
|
setDismissed([...dismissed, key]);
|
||||||
|
}
|
||||||
|
|
||||||
if (user.isStaff() && alerts.length > 0)
|
if (user.isStaff() && alerts.length > 0)
|
||||||
return (
|
return (
|
||||||
@@ -108,22 +67,7 @@ export function Alerts() {
|
|||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
{alerts.map((alert) => (
|
{alerts.map((alert) => (
|
||||||
<Menu.Item key={`alert-item-${alert.key}`}>
|
<Menu.Item key={`alert-item-${alert.key}`}>
|
||||||
<Alert
|
<ServerAlert alert={alert} closeAlert={closeAlert} />
|
||||||
withCloseButton
|
|
||||||
color={alert.error ? 'red' : 'orange'}
|
|
||||||
title={
|
|
||||||
<Group gap='xs'>
|
|
||||||
{alert.code && `${alert.code}: `}
|
|
||||||
{alert.title}
|
|
||||||
</Group>
|
|
||||||
}
|
|
||||||
onClose={() => setDismissed([...dismissed, alert.key])}
|
|
||||||
>
|
|
||||||
<Stack gap='xs'>
|
|
||||||
{alert.message}
|
|
||||||
{alert.code && errorCodeLink(alert.code)}
|
|
||||||
</Stack>
|
|
||||||
</Alert>
|
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
@@ -131,6 +75,84 @@ export function Alerts() {
|
|||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ServerAlert({
|
||||||
|
alert,
|
||||||
|
closeAlert
|
||||||
|
}: { alert: AlertInfo; closeAlert?: (key: string) => void }) {
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
withCloseButton={!!closeAlert}
|
||||||
|
color={alert.error ? 'red' : 'orange'}
|
||||||
|
title={
|
||||||
|
<Group gap='xs'>
|
||||||
|
{alert.code && `${alert.code}: `}
|
||||||
|
{alert.title}
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
onClose={closeAlert ? () => closeAlert(alert.key) : undefined}
|
||||||
|
>
|
||||||
|
<Stack gap='xs'>
|
||||||
|
{alert.message}
|
||||||
|
{alert.code && errorCodeLink(alert.code)}
|
||||||
|
</Stack>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtendedAlertInfo = AlertInfo & {
|
||||||
|
condition: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getAlerts(
|
||||||
|
server: ServerAPIProps,
|
||||||
|
globalSettings: SettingsStateProps,
|
||||||
|
inactive = false
|
||||||
|
): ExtendedAlertInfo[] {
|
||||||
|
const n_migrations =
|
||||||
|
Number.parseInt(globalSettings.getSetting('_PENDING_MIGRATIONS')) ?? 0;
|
||||||
|
|
||||||
|
const allalerts: ExtendedAlertInfo[] = [
|
||||||
|
{
|
||||||
|
key: 'debug',
|
||||||
|
title: t`Debug Mode`,
|
||||||
|
code: 'INVE-W4',
|
||||||
|
message: t`The server is running in debug mode.`,
|
||||||
|
condition: server?.debug_mode || false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'worker',
|
||||||
|
title: t`Background Worker`,
|
||||||
|
code: 'INVE-W5',
|
||||||
|
message: t`The background worker process is not running.`,
|
||||||
|
condition: !server?.worker_running
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'restart',
|
||||||
|
title: t`Server Restart`,
|
||||||
|
code: 'INVE-W6',
|
||||||
|
message: t`The server requires a restart to apply changes.`,
|
||||||
|
condition: globalSettings.isSet('SERVER_RESTART_REQUIRED')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
title: t`Email settings`,
|
||||||
|
code: 'INVE-W7',
|
||||||
|
message: t`Email settings not configured.`,
|
||||||
|
condition: !server?.email_configured
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'migrations',
|
||||||
|
title: t`Database Migrations`,
|
||||||
|
code: 'INVE-W8',
|
||||||
|
message: t`There are pending database migrations.`,
|
||||||
|
condition: n_migrations > 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return allalerts.filter((alert) => inactive || alert.condition);
|
||||||
|
}
|
||||||
|
|
||||||
export function errorCodeLink(code: string) {
|
export function errorCodeLink(code: string) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
|
|||||||
138
src/frontend/src/components/settings/QuickAction.tsx
Normal file
138
src/frontend/src/components/settings/QuickAction.tsx
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { Trans } from '@lingui/react/macro';
|
||||||
|
import { Button, Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
IconBrandGithub,
|
||||||
|
IconListCheck,
|
||||||
|
IconUserPlus,
|
||||||
|
IconUsersGroup,
|
||||||
|
type ReactNode
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
|
import { ApiEndpoints } from '@lib/index';
|
||||||
|
import {
|
||||||
|
projectCodeFields,
|
||||||
|
useCustomStateFields
|
||||||
|
} from '../../forms/CommonForms';
|
||||||
|
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||||
|
import { groupFields } from '../../tables/settings/GroupTable';
|
||||||
|
import { userFields } from '../../tables/settings/UserTable';
|
||||||
|
|
||||||
|
interface ActionItem {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon?: ReactNode;
|
||||||
|
buttonText?: string;
|
||||||
|
action: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActionGrid({ items }: { items: ActionItem[] }) {
|
||||||
|
const slides = items.map((image) => (
|
||||||
|
<Paper shadow='xs' p='sm' withBorder>
|
||||||
|
<Group justify='space-between' wrap='nowrap'>
|
||||||
|
<Stack>
|
||||||
|
<Text>
|
||||||
|
<strong>{image.title}</strong>
|
||||||
|
<br />
|
||||||
|
{image.description}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Button
|
||||||
|
size='sm'
|
||||||
|
variant='light'
|
||||||
|
onClick={image.action}
|
||||||
|
leftSection={image.icon}
|
||||||
|
>
|
||||||
|
{image.buttonText ?? <Trans>Act</Trans>}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SimpleGrid
|
||||||
|
cols={{
|
||||||
|
base: 1,
|
||||||
|
'600px': 2,
|
||||||
|
'1200px': 3
|
||||||
|
}}
|
||||||
|
type='container'
|
||||||
|
spacing='sm'
|
||||||
|
>
|
||||||
|
{slides}
|
||||||
|
</SimpleGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QuickAction = () => {
|
||||||
|
const newUser = useCreateApiFormModal(userFields());
|
||||||
|
const newGroup = useCreateApiFormModal(groupFields());
|
||||||
|
const newProjectCode = useCreateApiFormModal({
|
||||||
|
url: ApiEndpoints.project_code_list,
|
||||||
|
title: t`Add Project Code`,
|
||||||
|
fields: projectCodeFields()
|
||||||
|
});
|
||||||
|
const newCustomState = useCreateApiFormModal({
|
||||||
|
url: ApiEndpoints.custom_state_list,
|
||||||
|
title: t`Add State`,
|
||||||
|
fields: useCustomStateFields()
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
id: '0',
|
||||||
|
title: t`Open an Issue`,
|
||||||
|
description: t`Report a bug or request a feature on GitHub`,
|
||||||
|
icon: <IconBrandGithub />,
|
||||||
|
buttonText: t`Open Issue`,
|
||||||
|
action: () =>
|
||||||
|
window.open(
|
||||||
|
'https://github.com/inventree/inventree/issues/new',
|
||||||
|
'_blank'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: t`Add New Group`,
|
||||||
|
description: t`Create a new group to manage your users`,
|
||||||
|
icon: <IconUsersGroup />,
|
||||||
|
buttonText: t`New Group`,
|
||||||
|
action: () => newGroup.open()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: t`Add New User`,
|
||||||
|
description: t`Create a new user to manage your groups`,
|
||||||
|
icon: <IconUserPlus />,
|
||||||
|
buttonText: t`New User`,
|
||||||
|
action: () => newUser.open()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: t`Add Project Code`,
|
||||||
|
description: t`Create a new project code to organize your items`,
|
||||||
|
icon: <IconListCheck />,
|
||||||
|
buttonText: t`Add Code`,
|
||||||
|
action: () => newProjectCode.open()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
title: t`Add Custom State`,
|
||||||
|
description: t`Create a new custom state for your workflow`,
|
||||||
|
icon: <IconListCheck />,
|
||||||
|
buttonText: t`Add State`,
|
||||||
|
action: () => newCustomState.open()
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={'xs'} ml={'sm'}>
|
||||||
|
<ActionGrid items={items} />
|
||||||
|
{newUser.modal}
|
||||||
|
{newGroup.modal}
|
||||||
|
{newProjectCode.modal}
|
||||||
|
{newCustomState.modal}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
111
src/frontend/src/pages/Index/Settings/AdminCenter/HomePanel.tsx
Normal file
111
src/frontend/src/pages/Index/Settings/AdminCenter/HomePanel.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { Trans } from '@lingui/react/macro';
|
||||||
|
import { Accordion, Alert, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||||
|
import { type JSX, useMemo, useState } from 'react';
|
||||||
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
import { StylishText } from '../../../../components/items/StylishText';
|
||||||
|
import { ServerAlert, getAlerts } from '../../../../components/nav/Alerts';
|
||||||
|
import { QuickAction } from '../../../../components/settings/QuickAction';
|
||||||
|
import { useServerApiState } from '../../../../states/ServerApiState';
|
||||||
|
import { useGlobalSettingsState } from '../../../../states/SettingsStates';
|
||||||
|
|
||||||
|
export default function HomePanel(): JSX.Element {
|
||||||
|
const [dismissed, setDismissed] = useState<boolean>(false);
|
||||||
|
const [server] = useServerApiState(useShallow((state) => [state.server]));
|
||||||
|
const globalSettings = useGlobalSettingsState();
|
||||||
|
|
||||||
|
const accElements = useMemo(() => {
|
||||||
|
const _alerts = getAlerts(server, globalSettings, true);
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'active',
|
||||||
|
text: t`Active Alerts`,
|
||||||
|
elements: _alerts.filter((alert) => alert.condition)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'inactive',
|
||||||
|
text: t`Inactive Alerts`,
|
||||||
|
elements: _alerts.filter((alert) => !alert.condition)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, [server, globalSettings]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap='xs'>
|
||||||
|
{dismissed ? null : (
|
||||||
|
<Alert
|
||||||
|
color='blue'
|
||||||
|
title={t`Admin Center Information`}
|
||||||
|
withCloseButton
|
||||||
|
onClose={() => setDismissed(true)}
|
||||||
|
>
|
||||||
|
<Stack gap='xs'>
|
||||||
|
<Text>
|
||||||
|
<Trans>
|
||||||
|
The home panel (and the whole Admin Center) is a new feature
|
||||||
|
starting with the new UI and was previously (before 1.0) not
|
||||||
|
available.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Trans>
|
||||||
|
The admin center provides a centralized location for all
|
||||||
|
administration functionality and is meant to replace all
|
||||||
|
interaction with the (django) backend admin interface.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Trans>
|
||||||
|
Please open feature requests (after checking the tracker) for
|
||||||
|
any existing backend admin functionality you are missing in this
|
||||||
|
UI. The backend admin interface should be used carefully and
|
||||||
|
seldom.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<Accordion
|
||||||
|
multiple
|
||||||
|
defaultValue={['quick-actions', 'active']}
|
||||||
|
variant='contained'
|
||||||
|
>
|
||||||
|
<Accordion.Item value='quick-actions'>
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size='md'>
|
||||||
|
<Trans>Quick Actions</Trans>
|
||||||
|
</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<QuickAction />
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
{accElements.map(
|
||||||
|
(item) =>
|
||||||
|
item.elements.length > 0 && (
|
||||||
|
<Accordion.Item key={item.key} value={item.key}>
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size='md'>{item.text}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<SimpleGrid
|
||||||
|
cols={{
|
||||||
|
base: 1,
|
||||||
|
'600px': 2,
|
||||||
|
'1200px': 3
|
||||||
|
}}
|
||||||
|
type='container'
|
||||||
|
spacing='sm'
|
||||||
|
>
|
||||||
|
{item.elements.map((alert) => (
|
||||||
|
<ServerAlert alert={alert} />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Accordion>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
IconExclamationCircle,
|
IconExclamationCircle,
|
||||||
IconFileDownload,
|
IconFileDownload,
|
||||||
IconFileUpload,
|
IconFileUpload,
|
||||||
|
IconHome,
|
||||||
IconList,
|
IconList,
|
||||||
IconListDetails,
|
IconListDetails,
|
||||||
IconMail,
|
IconMail,
|
||||||
@@ -40,6 +41,8 @@ const ReportTemplatePanel = Loadable(
|
|||||||
|
|
||||||
const LabelTemplatePanel = Loadable(lazy(() => import('./LabelTemplatePanel')));
|
const LabelTemplatePanel = Loadable(lazy(() => import('./LabelTemplatePanel')));
|
||||||
|
|
||||||
|
const HomePanel = Loadable(lazy(() => import('./HomePanel')));
|
||||||
|
|
||||||
const UserManagementPanel = Loadable(
|
const UserManagementPanel = Loadable(
|
||||||
lazy(() => import('./UserManagementPanel'))
|
lazy(() => import('./UserManagementPanel'))
|
||||||
);
|
);
|
||||||
@@ -107,6 +110,13 @@ export default function AdminCenter() {
|
|||||||
|
|
||||||
const adminCenterPanels: PanelType[] = useMemo(() => {
|
const adminCenterPanels: PanelType[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
label: t`Home`,
|
||||||
|
icon: <IconHome />,
|
||||||
|
content: <HomePanel />,
|
||||||
|
showHeadline: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'user',
|
name: 'user',
|
||||||
label: t`Users / Access`,
|
label: t`Users / Access`,
|
||||||
@@ -231,6 +241,7 @@ export default function AdminCenter() {
|
|||||||
}, [user]);
|
}, [user]);
|
||||||
const grouping: PanelGroupType[] = useMemo(() => {
|
const grouping: PanelGroupType[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
{ id: 'home', label: '', panelIDs: ['home'] },
|
||||||
{
|
{
|
||||||
id: 'ops',
|
id: 'ops',
|
||||||
label: t`Operations`,
|
label: t`Operations`,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
|||||||
import { ModelType } from '@lib/enums/ModelType';
|
import { ModelType } from '@lib/enums/ModelType';
|
||||||
import { UserRoles } from '@lib/enums/Roles';
|
import { UserRoles } from '@lib/enums/Roles';
|
||||||
import { apiUrl } from '@lib/functions/Api';
|
import { apiUrl } from '@lib/functions/Api';
|
||||||
import { getDetailUrl } from '@lib/index';
|
import { type ApiFormModalProps, getDetailUrl } from '@lib/index';
|
||||||
import type { TableColumn } from '@lib/types/Tables';
|
import type { TableColumn, TableState } from '@lib/types/Tables';
|
||||||
import { IconUsersGroup } from '@tabler/icons-react';
|
import { IconUsersGroup } from '@tabler/icons-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { EditApiForm } from '../../components/forms/ApiForm';
|
import { EditApiForm } from '../../components/forms/ApiForm';
|
||||||
@@ -188,17 +188,7 @@ export function GroupTable({
|
|||||||
preFormWarning: t`Are you sure you want to delete this group?`
|
preFormWarning: t`Are you sure you want to delete this group?`
|
||||||
});
|
});
|
||||||
|
|
||||||
const newGroup = useCreateApiFormModal({
|
const newGroup = useCreateApiFormModal(groupFields(table));
|
||||||
url: ApiEndpoints.group_list,
|
|
||||||
title: t`Add Group`,
|
|
||||||
fields: {
|
|
||||||
name: {
|
|
||||||
label: t`Name`,
|
|
||||||
description: t`Name of the user group`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
table: table
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
@@ -256,3 +246,17 @@ export function GroupTable({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function groupFields(table?: TableState): ApiFormModalProps {
|
||||||
|
return {
|
||||||
|
url: ApiEndpoints.group_list,
|
||||||
|
title: t`Add Group`,
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
label: t`Name`,
|
||||||
|
description: t`Name of the user group`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
table: table ?? undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
|||||||
import { ModelType } from '@lib/enums/ModelType';
|
import { ModelType } from '@lib/enums/ModelType';
|
||||||
import { UserRoles } from '@lib/enums/Roles';
|
import { UserRoles } from '@lib/enums/Roles';
|
||||||
import { apiUrl } from '@lib/functions/Api';
|
import { apiUrl } from '@lib/functions/Api';
|
||||||
import { getDetailUrl } from '@lib/index';
|
import { type ApiFormModalProps, getDetailUrl } from '@lib/index';
|
||||||
import type { TableFilter } from '@lib/types/Filters';
|
import type { TableFilter } from '@lib/types/Filters';
|
||||||
import type { TableColumn } from '@lib/types/Tables';
|
import type { TableColumn, TableState } from '@lib/types/Tables';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
@@ -363,18 +363,7 @@ export function UserTable({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Table Actions - Add New User
|
// Table Actions - Add New User
|
||||||
const newUser = useCreateApiFormModal({
|
const newUser = useCreateApiFormModal(userFields(table));
|
||||||
url: ApiEndpoints.user_list,
|
|
||||||
title: t`Add User`,
|
|
||||||
fields: {
|
|
||||||
username: {},
|
|
||||||
email: {},
|
|
||||||
first_name: {},
|
|
||||||
last_name: {}
|
|
||||||
},
|
|
||||||
table: table,
|
|
||||||
successMessage: t`Added user`
|
|
||||||
});
|
|
||||||
|
|
||||||
const setPassword = useApiFormModal({
|
const setPassword = useApiFormModal({
|
||||||
url: ApiEndpoints.user_set_password,
|
url: ApiEndpoints.user_set_password,
|
||||||
@@ -468,6 +457,21 @@ export function UserTable({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function userFields(table?: TableState): ApiFormModalProps {
|
||||||
|
return {
|
||||||
|
url: ApiEndpoints.user_list,
|
||||||
|
title: t`Add User`,
|
||||||
|
fields: {
|
||||||
|
username: {},
|
||||||
|
email: {},
|
||||||
|
first_name: {},
|
||||||
|
last_name: {}
|
||||||
|
},
|
||||||
|
table: table ?? undefined,
|
||||||
|
successMessage: t`Added user`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function setUserActiveState(userId: number, active: boolean) {
|
async function setUserActiveState(userId: number, active: boolean) {
|
||||||
try {
|
try {
|
||||||
await api.patch(apiUrl(ApiEndpoints.user_list, userId), {
|
await api.patch(apiUrl(ApiEndpoints.user_list, userId), {
|
||||||
|
|||||||
Reference in New Issue
Block a user