feat: add full page index component with pagination and actions
- Implemented FullPagePageIndex component with mock data for database clusters. - Added pagination and bulk actions for managing database clusters. - Created navigation context hooks for detail, edit, duplicate, and create actions. - Introduced a new store for managing state in full-page and single-page modules. feat: add navigation localization files - Added English and Indonesian localization files for navigation menu items. - Included translations for various modules including CRM, Sales, Supply Chain, and more. feat: create system information shortcuts component - Developed Shortcut component to display keyboard shortcuts with search functionality. - Implemented System component to show placeholder information when system details are unavailable. - Added localization for shortcuts and system information in English and Indonesian. feat: implement global theme store - Created a Zustand store for managing theme color scheme with localStorage persistence. feat: add module page header component - Developed ModulePageHeader component for consistent page header across modules. - Included breadcrumb navigation, title, description, and action buttons. feat: define default privileges for enterprise module - Established default privileges for CRUD operations and other actions in the enterprise module.
This commit is contained in:
@@ -1,51 +1,77 @@
|
||||
import { memo, Fragment } from 'react';
|
||||
import { Group, Button, Menu, Divider, ActionIcon, Box } from '@mantine/core';
|
||||
import { ChevronDown, MoreVertical, X } from 'lucide-react';
|
||||
import { PageAction } from './types';
|
||||
import { Group, Button, Menu, Divider, ActionIcon, Box, ButtonProps, Tooltip } from '@mantine/core';
|
||||
import { ChevronDown, MoreVertical } from 'lucide-react';
|
||||
import { PageActionProps } from './types';
|
||||
import { getIntentColor } from './utils';
|
||||
|
||||
export interface PageActionsProps {
|
||||
/** Array of configured page-level actions. */
|
||||
actions: PageAction[];
|
||||
/** Optional callback triggered when the close (X) button is clicked. */
|
||||
onClose?: () => void;
|
||||
actions?: PageActionProps[];
|
||||
customButtonProps?: (action: PageActionProps) => ButtonProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* A responsive and flexible presentational component for page-level actions.
|
||||
* Automatically adapts layout based on screen size:
|
||||
* - Desktop: Renders a horizontal toolbar with buttons and dividers.
|
||||
* - Mobile: Renders a single Menu dropdown containing all actions.
|
||||
*
|
||||
* @performance Wrapped in React.memo to prevent unnecessary re-renders.
|
||||
* Automatically adapts layout based on screen size.
|
||||
*/
|
||||
export const PageActions = memo(function PageActions({ actions }: PageActionsProps) {
|
||||
export const PageActions = memo(function PageActions({ actions = [], customButtonProps }: PageActionsProps) {
|
||||
if (!actions || actions?.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function defaultButtonStyle(isPremiumGlow: boolean) {
|
||||
return {
|
||||
size: 'sm',
|
||||
radius: 'md',
|
||||
style: isPremiumGlow
|
||||
? { boxShadow: '0 4px 14px 0 color-mix(in srgb, var(--mantine-primary-color-filled) 40%, transparent)' }
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Box style={{ display: 'inline-flex' }}>
|
||||
{/* --- DESKTOP VIEW (hidden on mobile devices) --- */}
|
||||
{/* --- DESKTOP VIEW --- */}
|
||||
<Group gap="xs" wrap="nowrap" visibleFrom="sm">
|
||||
{actions.map((action, index) => {
|
||||
if (action.type === 'divider') {
|
||||
return <Divider key={`divider-${index}`} orientation="vertical" mr="sm" ml="sm" />;
|
||||
}
|
||||
|
||||
// Render Dropdown Menu for actions with children
|
||||
const isPremiumGlow = action.intent === 'primary' && action.variant === 'filled';
|
||||
|
||||
// 1. Button with Dropdown (Menu.Target)
|
||||
if (action.children && action.children.length > 0) {
|
||||
const ButtonWithDropdown = (
|
||||
<Button
|
||||
variant={action.variant || 'transparent'}
|
||||
color={getIntentColor(action.intent)}
|
||||
leftSection={action.icon}
|
||||
rightSection={<ChevronDown size={14} />}
|
||||
disabled={action.disabled}
|
||||
{...defaultButtonStyle(isPremiumGlow)}
|
||||
{...(customButtonProps ? customButtonProps(action) : {})}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu key={action.key} position="bottom-start" withArrow withinPortal trigger="hover">
|
||||
<Menu.Target>
|
||||
<Button
|
||||
variant={action.variant || 'transparent'}
|
||||
color={getIntentColor(action.intent)}
|
||||
leftSection={action.icon}
|
||||
rightSection={<ChevronDown size={14} />}
|
||||
disabled={action.disabled}
|
||||
size="xs"
|
||||
pr="sm"
|
||||
pl="sm"
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
{/* Shortcuts on the main button remain hidden in the Tooltip */}
|
||||
{action.shortcutLabel ? (
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
label={`${action.label} (${action.shortcutLabel})`}
|
||||
withArrow
|
||||
openDelay={500}
|
||||
>
|
||||
{ButtonWithDropdown}
|
||||
</Tooltip>
|
||||
) : (
|
||||
ButtonWithDropdown
|
||||
)}
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
{action.children.map((child, childIndex) => {
|
||||
@@ -69,33 +95,46 @@ export const PageActions = memo(function PageActions({ actions }: PageActionsPro
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
// 2. Regular Button (Standalone)
|
||||
const StandaloneButton = (
|
||||
<Button
|
||||
key={action.key}
|
||||
variant={action.variant || 'transparent'}
|
||||
color={getIntentColor(action.intent)}
|
||||
leftSection={action.icon}
|
||||
disabled={action.disabled}
|
||||
onClick={() => action.onClick?.(action.key || '')}
|
||||
size="xs"
|
||||
pr="sm"
|
||||
pl="sm"
|
||||
{...defaultButtonStyle(isPremiumGlow)}
|
||||
{...(customButtonProps ? customButtonProps(action) : {})}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return action.shortcutLabel ? (
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
key={action.key}
|
||||
label={`${action.label} (${action.shortcutLabel})`}
|
||||
withArrow
|
||||
openDelay={500}
|
||||
>
|
||||
{StandaloneButton}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Fragment key={action.key}>{StandaloneButton}</Fragment>
|
||||
);
|
||||
})}
|
||||
</Group>
|
||||
|
||||
{/* --- MOBILE VIEW (hidden on desktop devices) --- */}
|
||||
{/* --- MOBILE VIEW --- */}
|
||||
<Group gap="xs" wrap="nowrap" hiddenFrom="sm">
|
||||
<Menu position="bottom-end" withArrow withinPortal>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="transparent" size="md">
|
||||
<ActionIcon variant="outline" size="md">
|
||||
<MoreVertical size={16} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Dropdown px={'xl'}>
|
||||
{actions.map((action, index) => {
|
||||
if (action.type === 'divider') {
|
||||
return <Menu.Divider key={`mobile-divider-${index}`} mt="sm" mb="sm" />;
|
||||
@@ -116,7 +155,7 @@ export const PageActions = memo(function PageActions({ actions }: PageActionsPro
|
||||
color={getIntentColor(child.intent)}
|
||||
disabled={child.disabled}
|
||||
onClick={() => child.onClick?.(child.key || '')}
|
||||
style={{ paddingLeft: '1.5rem' }} // Indent nested items
|
||||
style={{ paddingLeft: '1.5rem' }}
|
||||
mt="sm"
|
||||
mb="sm"
|
||||
>
|
||||
@@ -137,6 +176,7 @@ export const PageActions = memo(function PageActions({ actions }: PageActionsPro
|
||||
onClick={() => action.onClick?.(action.key || '')}
|
||||
mt="sm"
|
||||
mb="sm"
|
||||
fw={600}
|
||||
>
|
||||
{action.label}
|
||||
</Menu.Item>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Fragment, memo } from 'react';
|
||||
import { Group, ActionIcon, Tooltip, Menu, Divider, Box, Button } from '@mantine/core';
|
||||
import { RowAction } from './types';
|
||||
import { RowActionProps } from './types';
|
||||
import { getIntentColor } from './utils';
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
|
||||
export interface RowActionsProps {
|
||||
/** Array of configured row-level actions. */
|
||||
actions: RowAction[];
|
||||
actions: RowActionProps[];
|
||||
showLabels?: boolean;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const RowActions = memo(function RowActions({ actions = [], showLabels =
|
||||
* Helper function to render a standalone icon button.
|
||||
* Wraps the icon in a Tooltip if the configuration provides one.
|
||||
*/
|
||||
const renderIcon = (action: RowAction, fallbackKey: string) => {
|
||||
const renderIcon = (action: RowActionProps, fallbackKey: string) => {
|
||||
const actionKey = action.key || fallbackKey;
|
||||
|
||||
const iconBtn = (
|
||||
|
||||
@@ -32,13 +32,15 @@ export interface BaseAction {
|
||||
* Specifically designed for toolbars, page headers, or detailed forms.
|
||||
* Enforces the presence of a text label (unless type is divider) and supports button-specific visual variants.
|
||||
*/
|
||||
export interface PageAction extends BaseAction {
|
||||
export interface PageActionProps extends BaseAction {
|
||||
/** Text label displayed on the button. Required for 'action' type. */
|
||||
label?: string;
|
||||
/** Specifies the Mantine button variant. Defaults to 'subtle'. */
|
||||
variant?: 'filled' | 'light' | 'outline' | 'default' | 'subtle' | 'transparent';
|
||||
/** Nested actions rendered as a Dropdown Menu below the main button. */
|
||||
children?: PageAction[];
|
||||
children?: PageActionProps[];
|
||||
/** Human-readable keyboard shortcut label (e.g., '⇧⌘N'). Shown in tooltip. */
|
||||
shortcutLabel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,11 +49,11 @@ export interface PageAction extends BaseAction {
|
||||
* Labels are optional (utilized inside dropdowns), supports hover tooltips,
|
||||
* and allows nested action hierarchies (e.g., Kebab menus).
|
||||
*/
|
||||
export interface RowAction extends BaseAction {
|
||||
export interface RowActionProps extends BaseAction {
|
||||
/** Optional text, primarily used when rendered inside a nested menu item. */
|
||||
label?: string;
|
||||
/** Optional text displayed on hover. */
|
||||
tooltip?: string;
|
||||
/** Nested actions that will be rendered inside a dropdown menu. */
|
||||
children?: RowAction[];
|
||||
children?: RowActionProps[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user