refactor: update icons and improve action visibility in row and bulk action menus

This commit is contained in:
Firman Ramdhani
2026-07-23 18:29:40 +07:00
parent 7132ebc025
commit 1812f7c585
6 changed files with 51 additions and 174 deletions
@@ -1,131 +0,0 @@
import { Paper, SimpleGrid, Box, Text, Group, Badge, Divider, Breadcrumbs, Anchor, Flex, Title, ActionIcon, Button } from '@repo/ui/components';
import { useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
import { ChevronRight, CheckCircle2, Trash2, Edit2, Play } from 'lucide-react';
export default function FullPagePageDetail() {
const { t } = useEnterpriseModuleTranslationContext();
return (
<Box pb="xl">
{/* --- INLINED PAGE HEADER --- */}
<Box mb="xl" mt="xs">
<Breadcrumbs
mb="md"
separator={<ChevronRight size={12} strokeWidth={3} style={{ color: 'var(--mantine-color-gray-5)' }} />}
>
<Anchor href="#" c="dimmed" size="xs" fw={500} style={{ transition: 'color 0.2s ease', letterSpacing: '0.2px' }}>
Acme Corp
</Anchor>
<Anchor href="#" c="dimmed" size="xs" fw={500} style={{ transition: 'color 0.2s ease', letterSpacing: '0.2px' }}>
Infrastructure
</Anchor>
<Anchor href="#" c="dimmed" size="xs" fw={500} style={{ transition: 'color 0.2s ease', letterSpacing: '0.2px' }}>
Database Clusters
</Anchor>
<Text size="xs" fw={600} style={{ color: 'light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0))', letterSpacing: '0.2px' }}>
prod-db-01
</Text>
</Breadcrumbs>
<Group justify="space-between" align="center" wrap="nowrap">
<Flex gap="md" align="center">
<Box>
<Group gap="xs" align="center" mb={4}>
<Title
order={2}
fw={600}
style={{
color: 'light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0))',
letterSpacing: '-0.3px',
}}
>
prod-db-01
</Title>
<Badge
variant="light"
color="teal"
size="sm"
radius="sm"
fw={600}
leftSection={<CheckCircle2 size={10} strokeWidth={3} />}
style={{ textTransform: 'capitalize' }}
>
Running
</Badge>
</Group>
<Text size="sm" c="dimmed" fw={400}>
PostgreSQL v15.4 · <Text span fw={500} c="var(--mantine-color-text)">us-east-1</Text> · Created Oct 12, 2023
</Text>
</Box>
</Flex>
<Group gap="sm">
<ActionIcon variant="light" color="red" size="lg" radius="md" aria-label="Delete">
<Trash2 size={18} strokeWidth={2} />
</ActionIcon>
<Button
variant="default"
radius="md"
leftSection={<Edit2 size={16} strokeWidth={2} style={{ color: 'var(--mantine-color-dimmed)' }} />}
>
Edit
</Button>
<Button
variant="filled"
color="indigo"
radius="md"
leftSection={<Play size={16} strokeWidth={2.5} />}
style={{ boxShadow: '0 4px 14px 0 rgba(76, 110, 245, 0.39)' }}
>
Restart
</Button>
</Group>
</Group>
<Divider mt={28} mb={0} color="light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))" />
</Box>
{/* --- END INLINED PAGE HEADER --- */}
<Paper withBorder shadow="sm" radius="md" p="xl">
<Text size="lg" fw={600} mb="xs" style={{ color: 'light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0))', letterSpacing: '-0.3px' }}>
General Information
</Text>
<Text size="sm" c="dimmed" mb="xl">
View the complete details and configuration for this entity.
</Text>
<Divider mb="xl" color="light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))" />
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="xl" verticalSpacing="xl">
<Box>
<Text size="xs" tt="uppercase" fw={700} c="dimmed" mb={4} style={{ letterSpacing: '0.5px' }}>
{t('fields.code')}
</Text>
<Text size="sm" fw={500}>WID-001</Text>
</Box>
<Box>
<Text size="xs" tt="uppercase" fw={700} c="dimmed" mb={4} style={{ letterSpacing: '0.5px' }}>
{t('fields.name')}
</Text>
<Text size="sm" fw={500}>Dashboard Widget</Text>
</Box>
<Box>
<Text size="xs" tt="uppercase" fw={700} c="dimmed" mb={4} style={{ letterSpacing: '0.5px' }}>
{t('fields.status')}
</Text>
<Badge variant="light" color="teal" size="sm" radius="sm" fw={600}>
ACTIVE
</Badge>
</Box>
<Box>
<Text size="xs" tt="uppercase" fw={700} c="dimmed" mb={4} style={{ letterSpacing: '0.5px' }}>
{t('fields.description')}
</Text>
<Text size="sm" fw={500}>Main dashboard widget</Text>
</Box>
</SimpleGrid>
</Paper>
</Box>
);
}
@@ -1,5 +1,5 @@
import { Fragment, memo } from 'react';
import { Group, ActionIcon, Tooltip, Menu, Divider, Box, Button } from '@mantine/core';
import { Group, ActionIcon, Tooltip, Menu, Divider, Box, Button, MantineSpacing } from '@mantine/core';
import { RowActionProps } from './types';
import { getIntentColor } from './utils';
import { MoreVertical } from 'lucide-react';
@@ -9,6 +9,8 @@ export interface RowActionsProps {
actions: RowActionProps[];
showLabels?: boolean;
responsiveView?: boolean;
gapActionDesktop?: MantineSpacing;
gapActionMobile?: MantineSpacing;
}
/**
@@ -17,11 +19,9 @@ export interface RowActionsProps {
*
* @performance Wrapped in React.memo to guarantee zero overhead inside large lists/grids.
*/
export const RowActions = memo(function RowActions({
actions = [],
showLabels = false,
responsiveView = true,
}: RowActionsProps) {
export const RowActions = memo(function RowActions(props: RowActionsProps) {
const { actions = [], showLabels = false, responsiveView = true, gapActionDesktop = 0, gapActionMobile = 0 } = props;
/**
* Helper function to render a standalone item.
*/
@@ -114,14 +114,14 @@ export const RowActions = memo(function RowActions({
return (
<Box style={{ display: 'inline-flex' }}>
{/* --- DESKTOP VIEW (hidden on mobile devices) --- */}
<Group gap={0} wrap="nowrap" visibleFrom={responsiveView ? 'sm' : undefined}>
<Group gap={gapActionDesktop} wrap="nowrap" visibleFrom={responsiveView ? 'sm' : undefined}>
{renderFlatActions()}
</Group>
{/* --- MOBILE VIEW (hidden on desktop devices) --- */}
{responsiveView && (
<Group gap={0} wrap="nowrap" hiddenFrom="sm">
<Group gap={gapActionMobile} wrap="nowrap" hiddenFrom="sm">
<Menu position="bottom-end" withArrow withinPortal zIndex={9999}>
<Menu.Target>
<ActionIcon variant="transparent" size="md">
@@ -4,7 +4,7 @@ import {
useEnterpriseModuleTranslationContext,
useEnterpriseModuleConfigContext,
} from '../../../hooks/use-module.context';
import { Trash2, CheckCircle, XCircle, PauseCircle, RotateCcw, X, Check } from 'lucide-react';
import { Trash, Unlock, Lock, PauseCircle, RotateCcw, X, Check } from 'lucide-react';
import { PageActions, PageActionProps } from '../../../../../components';
// ---------------------------------------------------------------------------
@@ -29,7 +29,7 @@ export interface BulkActionMenuProps {
* privilege checks, and the status of selected rows — mirroring the
* row-level action logic in `RowActionMenu`.
*
* All actions have `showLabel: false` so they render as icon-only
* All actions have `showLabel: t so they render as icon-only
* `ActionIcon` buttons via the `PageActions` component.
*
* @performance
@@ -74,9 +74,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.DEACTIVATE,
label: t('common:actions.deactivate'),
icon: <XCircle size={16} />,
icon: <Lock size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.DEACTIVATE),
});
}
@@ -85,9 +85,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.ACTIVATE,
label: t('common:actions.activate'),
icon: <CheckCircle size={16} />,
icon: <Unlock size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.ACTIVATE),
});
}
@@ -99,9 +99,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.HOLD,
label: t('common:actions.hold'),
icon: <PauseCircle size={16} />,
icon: <PauseCircle size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.HOLD),
});
}
@@ -110,9 +110,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.ROLLBACK,
label: t('common:actions.rollback'),
icon: <RotateCcw size={16} />,
icon: <RotateCcw size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.ROLLBACK),
});
}
@@ -121,9 +121,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.CANCEL,
label: t('common:actions.cancel'),
icon: <X size={16} />,
icon: <X size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.CANCEL),
});
}
@@ -132,9 +132,9 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.CONFIRM,
label: t('common:actions.confirm'),
icon: <Check size={16} />,
icon: <Check size={14} />,
variant: 'default',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.CONFIRM),
});
}
@@ -145,10 +145,10 @@ export function BulkActionMenu({
defaultActions.push({
key: ModuleAction.DELETE,
label: t('common:actions.delete'),
icon: <Trash2 size={16} />,
icon: <Trash size={14} />,
variant: 'outline',
intent: 'destructive',
showLabel: false,
showLabel: true,
onClick: handleClick(ModuleAction.DELETE),
});
}
@@ -158,5 +158,13 @@ export function BulkActionMenu({
if (actions.length === 0) return null;
return <PageActions actions={actions} />;
return (
<PageActions
actions={actions}
gapActionDesktop={'sm'}
customButtonProps={() => {
return { size: 'xs' };
}}
/>
);
}
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { Eye, Trash2, CheckCircle, XCircle, Edit2, Copy, PauseCircle, RotateCcw, X, Check } from 'lucide-react';
import { FileSearchCorner, Trash, Unlock, Lock, SquarePen, Copy, PauseCircle, RotateCcw, X, Check } from 'lucide-react';
import { ModuleAction, ModuleActionType } from '../../../entities/entity';
import {
useEnterpriseModuleTranslationContext,
@@ -47,7 +47,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: 'VIEW',
label: t('common:actions.detail'),
tooltip: t('common:actions.detail'),
icon: <Eye size={14} />,
icon: <FileSearchCorner size={15} />,
onClick: () => onActionClick('VIEW' as any, data),
});
@@ -56,7 +56,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.EDIT,
label: t('common:actions.edit'),
tooltip: t('common:actions.edit'),
icon: <Edit2 size={14} />,
icon: <SquarePen size={15} />,
onClick: () => onActionClick(ModuleAction.EDIT, data),
});
}
@@ -66,7 +66,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.DUPLICATE,
label: t('common:actions.duplicate'),
tooltip: t('common:actions.duplicate'),
icon: <Copy size={14} />,
icon: <Copy size={15} />,
onClick: () => onActionClick(ModuleAction.DUPLICATE, data),
});
}
@@ -77,7 +77,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.DEACTIVATE,
label: t('common:actions.deactivate'),
tooltip: t('common:actions.deactivate'),
icon: <XCircle size={14} />,
icon: <Lock size={15} />,
onClick: () => onActionClick(ModuleAction.DEACTIVATE, data),
});
}
@@ -86,7 +86,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.ACTIVATE,
label: t('common:actions.activate'),
tooltip: t('common:actions.activate'),
icon: <CheckCircle size={14} />,
icon: <Unlock size={15} />,
onClick: () => onActionClick(ModuleAction.ACTIVATE, data),
});
}
@@ -98,7 +98,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.HOLD,
label: t('common:actions.hold'),
tooltip: t('common:actions.hold'),
icon: <PauseCircle size={14} />,
icon: <PauseCircle size={15} />,
onClick: () => onActionClick(ModuleAction.HOLD, data),
});
}
@@ -107,7 +107,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.ROLLBACK,
label: t('common:actions.rollback'),
tooltip: t('common:actions.rollback'),
icon: <RotateCcw size={14} />,
icon: <RotateCcw size={15} />,
onClick: () => onActionClick(ModuleAction.ROLLBACK, data),
});
}
@@ -116,7 +116,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.CANCEL,
label: t('common:actions.cancel'),
tooltip: t('common:actions.cancel'),
icon: <X size={14} />,
icon: <X size={15} />,
onClick: () => onActionClick(ModuleAction.CANCEL, data),
});
}
@@ -125,7 +125,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.CONFIRM,
label: t('common:actions.confirm'),
tooltip: t('common:actions.confirm'),
icon: <Check size={14} />,
icon: <Check size={15} />,
onClick: () => onActionClick(ModuleAction.CONFIRM, data),
});
}
@@ -136,7 +136,7 @@ export function RowActionMenu({ data, onActionClick, statusKey = 'status', custo
key: ModuleAction.DELETE,
label: t('common:actions.delete'),
tooltip: t('common:actions.delete'),
icon: <Trash2 size={14} />,
icon: <Trash size={15} />,
intent: 'destructive',
onClick: () => onActionClick(ModuleAction.DELETE, data),
});
@@ -824,7 +824,7 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
{
key: 'filter',
icon: (
<Indicator disabled={activeFilterCount === 0} size={8} offset={2}>
<Indicator disabled={activeFilterCount === 0} size={8} offset={2} color="yellow">
<Filter size={16} />
</Indicator>
),
@@ -881,7 +881,7 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
statusBar={statusBar}
domLayout={domLayout}
overlayLoadingTemplate={`<span style="padding:10px">${loadingMessage}</span>`}
overlayNoRowsTemplate={`<span style="padding:10px;color:var(--ag-foreground-color,#868e96)">${noRowsMessage}</span>`}
overlayNoRowsTemplate={`<span style="padding:10px;color:var(--ag-foreground-color,#868e96);">${noRowsMessage}</span>`}
{...restAgGridProps}
/>
</Box>
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { BaseEntity } from '@repo/core-api/data-services';
import { Check, CheckCircle, Copy, Edit2, PauseCircle, Plus, RotateCcw, Trash2, X, XCircle } from 'lucide-react';
import { Check, Unlock, Copy, SquarePen, PauseCircle, Plus, RotateCcw, Trash, X, Lock } from 'lucide-react';
import { DetailPageContext } from '../hooks/use-detail-page.context';
import { EnterpriseDetailPageConfig, ModuleAction, ModuleActionType, ActionModalState } from '../entities/entity';
@@ -421,7 +421,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
ALLOW_DELETE && {
key: ModuleAction.DELETE,
label: t('common:actions.delete'),
icon: <Trash2 size={16} />,
icon: <Trash size={16} />,
intent: 'destructive',
variant: 'subtle',
onClick: (key: ModuleActionType) => handleActionClick(key),
@@ -432,7 +432,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
isDataActive && {
key: ModuleAction.DEACTIVATE,
label: t('common:actions.deactivate'),
icon: <XCircle size={16} />,
icon: <Lock size={16} />,
intent: 'default',
variant: 'subtle',
onClick: (key: ModuleActionType) => handleActionClick(key),
@@ -443,7 +443,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
key: ModuleAction.ACTIVATE,
label: t('common:actions.activate'),
tooltipLabel: t('common:actions.activate'),
icon: <CheckCircle size={16} />,
icon: <Unlock size={16} />,
intent: 'default',
variant: 'subtle',
onClick: (key: ModuleActionType) => handleActionClick(key),
@@ -460,7 +460,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
ALLOW_EDIT && {
key: ModuleAction.EDIT,
label: t('common:actions.edit'),
icon: <Edit2 size={16} />,
icon: <SquarePen size={16} />,
intent: 'default',
variant: 'subtle',
onClick: (key: ModuleActionType) => handleActionClick(key),