4410-feat(front): Implement Confirmation Prompt for Multiple Record Deletion (#4514)

* feat: implement confirmation prompt

* feat: remove prop drilling and introduce recoil state

* chore: fix eslint issues

* feat: set record text according to length of records

* chore: fix eslint issues

* refactor: made changes according to code review

* fix: show delete according to singular and plural records.

* fix: eslint issues

* feat: show number of selected records

* style: fix positioning of actionbar

* feat: display ConfirmationModal seperately

* chore: remove recoil state and use usestate instead

* chore: minor change

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Anchit Sinha
2024-03-29 22:48:21 +05:30
committed by GitHub
parent aaf429a907
commit 7f3623239a
5 changed files with 64 additions and 17 deletions

View File

@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { isNonEmptyString } from '@sniptt/guards'; import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilCallback, useSetRecoilState } from 'recoil'; import { useRecoilCallback, useSetRecoilState } from 'recoil';
@ -17,6 +17,7 @@ import {
IconPuzzle, IconPuzzle,
IconTrash, IconTrash,
} from '@/ui/display/icon'; } from '@/ui/display/icon';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState'; import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState'; import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
import { ContextMenuEntry } from '@/ui/navigation/context-menu/types/ContextMenuEntry'; import { ContextMenuEntry } from '@/ui/navigation/context-menu/types/ContextMenuEntry';
@ -36,6 +37,8 @@ export const useRecordActionBar = ({
}: useRecordActionBarProps) => { }: useRecordActionBarProps) => {
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState); const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState); const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
useState(false);
const { createFavorite, favorites, deleteFavorite } = useFavorites(); const { createFavorite, favorites, deleteFavorite } = useFavorites();
@ -106,10 +109,26 @@ export const useRecordActionBar = ({
const baseActions: ContextMenuEntry[] = useMemo( const baseActions: ContextMenuEntry[] = useMemo(
() => [ () => [
{ {
label: `Delete (${selectedRecordIds.length})`, label: 'Delete',
Icon: IconTrash, Icon: IconTrash,
accent: 'danger', accent: 'danger',
onClick: () => handleDeleteClick(), onClick: () => setIsDeleteRecordsModalOpen(true),
ConfirmationModal: (
<ConfirmationModal
isOpen={isDeleteRecordsModalOpen}
setIsOpen={setIsDeleteRecordsModalOpen}
title={`Delete ${selectedRecordIds.length} ${
selectedRecordIds.length === 1 ? `record` : 'records'
}`}
subtitle={`This action cannot be undone. This will permanently delete ${
selectedRecordIds.length === 1 ? 'this record' : 'these records'
}`}
onConfirmClick={() => handleDeleteClick()}
deleteButtonText={`Delete ${
selectedRecordIds.length > 1 ? 'Records' : 'Record'
}`}
/>
),
}, },
{ {
label: `${progress === undefined ? `Export` : `Export (${progress}%)`}`, label: `${progress === undefined ? `Export` : `Export (${progress}%)`}`,
@ -118,7 +137,14 @@ export const useRecordActionBar = ({
onClick: () => download(), onClick: () => download(),
}, },
], ],
[handleDeleteClick, download, progress, selectedRecordIds], [
handleDeleteClick,
download,
progress,
selectedRecordIds,
isDeleteRecordsModalOpen,
setIsDeleteRecordsModalOpen,
],
); );
const dataExecuteQuickActionOnmentEnabled = useIsFeatureEnabled( const dataExecuteQuickActionOnmentEnabled = useIsFeatureEnabled(

View File

@ -18,5 +18,5 @@ export const RecordBoardActionBar = ({
return null; return null;
} }
return <ActionBar />; return <ActionBar selectedIds={selectedRecordIds} />;
}; };

View File

@ -16,5 +16,5 @@ export const RecordTableActionBar = ({
return null; return null;
} }
return <ActionBar />; return <ActionBar selectedIds={selectedRowIds} />;
}; };

View File

@ -1,4 +1,4 @@
import React, { useRef } from 'react'; import { useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
@ -7,6 +7,10 @@ import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/cont
import { ActionBarItem } from './ActionBarItem'; import { ActionBarItem } from './ActionBarItem';
type ActionBarProps = {
selectedIds?: string[];
};
const StyledContainerActionBar = styled.div` const StyledContainerActionBar = styled.div`
align-items: center; align-items: center;
background: ${({ theme }) => theme.background.secondary}; background: ${({ theme }) => theme.background.secondary};
@ -27,7 +31,15 @@ const StyledContainerActionBar = styled.div`
z-index: 1; z-index: 1;
`; `;
export const ActionBar = () => { const StyledLabel = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
`;
export const ActionBar = ({ selectedIds }: ActionBarProps) => {
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState); const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
const actionBarEntries = useRecoilValue(actionBarEntriesState); const actionBarEntries = useRecoilValue(actionBarEntriesState);
const wrapperRef = useRef<HTMLDivElement>(null); const wrapperRef = useRef<HTMLDivElement>(null);
@ -37,14 +49,22 @@ export const ActionBar = () => {
} }
return ( return (
<StyledContainerActionBar <>
data-select-disable <StyledContainerActionBar
className="action-bar" data-select-disable
ref={wrapperRef} className="action-bar"
> ref={wrapperRef}
{actionBarEntries.map((item, index) => ( >
<ActionBarItem key={index} item={item} /> {selectedIds && (
))} <StyledLabel>{selectedIds.length} selected:</StyledLabel>
</StyledContainerActionBar> )}
{actionBarEntries.map((item, index) => (
<ActionBarItem key={index} item={item} />
))}
</StyledContainerActionBar>
<div data-select-disable className="action-bar">
{actionBarEntries[0]?.ConfirmationModal}
</div>
</>
); );
}; };

View File

@ -7,4 +7,5 @@ export type ActionBarEntry = {
accent?: MenuItemAccent; accent?: MenuItemAccent;
onClick?: () => void; onClick?: () => void;
subActions?: ActionBarEntry[]; subActions?: ActionBarEntry[];
ConfirmationModal?: JSX.Element;
}; };