feat: enhance enterprise module with new actions and UI improvements

- Added support for rollback and hold actions in the enterprise module.
- Updated tooltip labels for action buttons to improve user experience.
- Refactored PageActions component to use tooltipLabel instead of shortcutLabel.
- Introduced height prop for system pages (Coming Soon, Forbidden, Maintenance, Not Found) for better layout control.
- Improved ModulePageHeader to accept custom button properties and enhanced title handling.
- Cleaned up default privileges by removing unused actions (ALLOW_DUPLICATE, ALLOW_SAVE).
- Implemented detail page provider with comprehensive action handling for CRUD operations.
- Created a new full-page detail component for better data presentation.
This commit is contained in:
Firman Ramdhani
2026-07-15 15:28:58 +07:00
parent 383e2627e0
commit 7ce4ced7d6
24 changed files with 741 additions and 208 deletions
@@ -60,13 +60,8 @@ export const PageActions = memo(function PageActions({ actions = [], customButto
<Menu key={action.key} position="bottom-start" withArrow withinPortal trigger="hover">
<Menu.Target>
{/* Shortcuts on the main button remain hidden in the Tooltip */}
{action.shortcutLabel ? (
<Tooltip
position="bottom"
label={`${action.label} (${action.shortcutLabel})`}
withArrow
openDelay={500}
>
{action.tooltipLabel ? (
<Tooltip position="bottom" label={`${action.tooltipLabel}`} withArrow openDelay={500}>
{ButtonWithDropdown}
</Tooltip>
) : (
@@ -110,14 +105,8 @@ export const PageActions = memo(function PageActions({ actions = [], customButto
</Button>
);
return action.shortcutLabel ? (
<Tooltip
position="bottom"
key={action.key}
label={`${action.label} (${action.shortcutLabel})`}
withArrow
openDelay={500}
>
return action.tooltipLabel ? (
<Tooltip position="bottom" key={action.key} label={`${action.tooltipLabel}`} withArrow openDelay={500}>
{StandaloneButton}
</Tooltip>
) : (
@@ -39,8 +39,9 @@ export interface PageActionProps extends BaseAction {
variant?: 'filled' | 'light' | 'outline' | 'default' | 'subtle' | 'transparent';
/** Nested actions rendered as a Dropdown Menu below the main button. */
children?: PageActionProps[];
/** Human-readable keyboard shortcut label (e.g., '⇧⌘N'). Shown in tooltip. */
shortcutLabel?: string;
/** Human-readable keyboard tooltip label (e.g., '⇧⌘N'). Shown in tooltip. */
tooltipLabel?: string;
}
/**
@@ -1,12 +1,13 @@
interface ComingSoonProps {
showActions?: boolean;
height?: StyleProp<React.CSSProperties['height']>;
}
import { Title, Text, Button, Container, Stack, Center } from '@mantine/core';
import { Title, Text, Button, Container, Stack, Center, StyleProp } from '@mantine/core';
export function ComingSoon({ showActions = false }: ComingSoonProps) {
export function ComingSoon({ showActions = false, height = '100vh' }: ComingSoonProps) {
return (
<Container size="sm" h="100vh" pos="relative">
<Container size="sm" h={height} pos="relative">
<Center h="100%">
<Stack align="center" ta="center" gap="md">
<Text size="sm" fw={500} tt="uppercase" c="dimmed" lts="var(--mantine-spacing-xs)">
@@ -22,7 +23,7 @@ export function ComingSoon({ showActions = false }: ComingSoonProps) {
</Text>
{showActions && (
<Button component="a" href="/" color="brand" size="md" mt="xl">
<Button component="a" href="/" color="brand" size="sm" mt="xl">
Back to Home
</Button>
)}
@@ -4,9 +4,10 @@ interface ForbiddenProps {
onClickGoBack?(): void;
onClickBackToHome?(): void;
homeUrl?: string;
height?: StyleProp<React.CSSProperties['height']>;
}
import { Title, Text, Button, Container, Stack, Group, Center } from '@mantine/core';
import { Title, Text, Button, Container, Stack, Group, Center, StyleProp } from '@mantine/core';
import { useNavigate } from 'react-router-dom';
export function Forbidden({
@@ -15,6 +16,7 @@ export function Forbidden({
onClickGoBack,
onClickBackToHome,
homeUrl,
height = '100vh',
}: ForbiddenProps) {
const navigate = useNavigate();
@@ -28,7 +30,7 @@ export function Forbidden({
else if (homeUrl) navigate(homeUrl, { replace: true });
}
return (
<Container size="sm" h="100vh" pos="relative">
<Container size="sm" h={height} pos="relative">
<Center h="100%">
<Stack align="center" ta="center" gap="md">
<Text size="sm" fw={500} tt="uppercase" c="dimmed" lts="var(--mantine-spacing-xs)">
@@ -47,13 +49,13 @@ export function Forbidden({
{(showActionsBack || showActionsHome) && (
<Group mt="xl" justify="center">
{showActionsBack && (
<Button variant="default" size="md" onClick={onBack}>
<Button variant="default" size="sm" onClick={onBack}>
Go Back
</Button>
)}
{showActionsHome && (
<Button color="brand" size="md" onClick={onBackHome}>
<Button color="brand" size="sm" onClick={onBackHome}>
Back to Home
</Button>
)}
@@ -1,12 +1,13 @@
interface MaintenanceProps {
showActions?: boolean;
height?: StyleProp<React.CSSProperties['height']>;
}
import { Title, Text, Button, Container, Stack, Center } from '@mantine/core';
import { Title, Text, Button, Container, Stack, Center, StyleProp } from '@mantine/core';
export function Maintenance({ showActions = false }: MaintenanceProps) {
export function Maintenance({ showActions = false, height = '100vh' }: MaintenanceProps) {
return (
<Container size="sm" h="100vh" pos="relative">
<Container size="sm" h={height} pos="relative">
<Center h="100%">
<Stack align="center" ta="center" gap="md">
<Text size="sm" fw={500} tt="uppercase" c="dimmed" lts="var(--mantine-spacing-xs)">
@@ -23,7 +24,7 @@ export function Maintenance({ showActions = false }: MaintenanceProps) {
</Text>
{showActions && (
<Button component="a" href="/" color="brand" size="md" mt="xl">
<Button component="a" href="/" color="brand" size="sm" mt="xl">
Back to Home
</Button>
)}
@@ -4,9 +4,10 @@ interface NotFoundProps {
onClickGoBack?(): void;
onClickBackToHome?(): void;
homeUrl?: string;
height?: StyleProp<React.CSSProperties['height']>;
}
import { Title, Text, Button, Container, Stack, Group, Center } from '@mantine/core';
import { Title, Text, Button, Container, Stack, Group, Center, StyleProp } from '@mantine/core';
import { useNavigate } from 'react-router-dom';
export function NotFound({
@@ -15,6 +16,7 @@ export function NotFound({
onClickGoBack,
onClickBackToHome,
homeUrl,
height = '100vh',
}: NotFoundProps) {
const navigate = useNavigate();
@@ -29,7 +31,7 @@ export function NotFound({
}
return (
<Container size="sm" h="100vh" pos="relative">
<Container size="sm" h={height} pos="relative">
<Center h="100%">
<Stack align="center" ta="center" gap="md">
<Text size="sm" fw={500} tt="uppercase" c="dimmed" lts="var(--mantine-spacing-xs)">
@@ -47,13 +49,13 @@ export function NotFound({
{(showActionsBack || showActionsHome) && (
<Group mt="xl" justify="center">
{showActionsBack && (
<Button variant="default" size="md" onClick={onBack}>
<Button variant="default" size="sm" onClick={onBack}>
Go Back
</Button>
)}
{showActionsHome && (
<Button color="brand" size="md" onClick={onBackHome}>
<Button color="brand" size="sm" onClick={onBackHome}>
Back to Home
</Button>
)}