Merge pull request 'refactor: update icons and improve action visibility in row and bulk action menus' (#30) from core/page-provider into main
Reviewed-on: eigen/fe-monorepo-template#30
This commit is contained in:
-131
@@ -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">
|
||||
|
||||
+25
-17
@@ -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' };
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+11
-11
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user