Merge branch 'fix/isloading-photos' into 'main'
[ProtonMail-WebClient.git] / packages / components / containers / contacts / merge / ContactMergeTableContent.tsx
blob4afe531d8eb7af7cc28bdfb5c43e5fd656a39ff0
1 import type { Dispatch, SetStateAction } from 'react';
3 import { c } from 'ttag';
5 import { Button } from '@proton/atoms';
6 import Alert from '@proton/components/components/alert/Alert';
7 import ModalTwoContent from '@proton/components/components/modalTwo/ModalContent';
8 import ModalTwoFooter from '@proton/components/components/modalTwo/ModalFooter';
9 import ModalTwoHeader from '@proton/components/components/modalTwo/ModalHeader';
10 import type { ContactFormatted, ContactMergeModel } from '@proton/shared/lib/interfaces/contacts';
11 import move from '@proton/utils/move';
13 import type { ContactMergePreviewModalProps } from './ContactMergePreviewModal';
14 import MergeTable from './table/MergeTable';
16 interface Props {
17     model: ContactMergeModel;
18     updateModel: Dispatch<SetStateAction<ContactMergeModel>>;
19     onSubmit: () => void;
20     onClose?: () => void;
21     beMergedModel: { [ID: string]: string[] };
22     beDeletedModel: { [ID: string]: string };
23     totalBeMerged: number;
24     totalBeDeleted: number;
25     onMergeDetails: (contactID: string) => void;
26     onMergePreview: (props: ContactMergePreviewModalProps) => void;
29 /**
30  * Re-order elements in an array inside a group of arrays
31  */
32 const moveInGroup = (
33     collection: ContactFormatted[][],
34     groupIndex: number,
35     { oldIndex, newIndex }: { oldIndex: number; newIndex: number }
36 ) => {
37     return collection.map((group, i) => {
38         if (i === groupIndex) {
39             return move(group, oldIndex, newIndex);
40         }
41         return group;
42     });
45 const ContactMergeTableContent = ({
46     model,
47     updateModel,
48     onSubmit,
49     onClose,
50     beMergedModel,
51     beDeletedModel,
52     totalBeMerged,
53     totalBeDeleted,
54     onMergeDetails,
55     onMergePreview,
56 }: Props) => {
57     const { orderedContacts, isChecked, beDeleted } = model;
59     const isDeleteOnly = totalBeMerged <= 0 && totalBeDeleted > 0;
61     const handleToggleCheck = (ID: string) => {
62         updateModel((model) => ({
63             ...model,
64             isChecked: { ...isChecked, [ID]: !isChecked[ID] },
65         }));
66     };
67     const handleToggleDelete = (ID: string) => {
68         updateModel((model) => ({
69             ...model,
70             beDeleted: { ...beDeleted, [ID]: !beDeleted[ID] },
71         }));
72     };
73     const handleSortEnd =
74         (groupIndex: number) =>
75         ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
76             updateModel((model) => ({
77                 ...model,
78                 orderedContacts: moveInGroup(orderedContacts, groupIndex, { oldIndex, newIndex }),
79             }));
80         };
82     const handlePreview = (beMergedID: string, beDeletedIDs: string[]) => {
83         const beMergedModelSingle = { [beMergedID]: beMergedModel[beMergedID] };
84         const beDeletedModelSingle = beDeletedIDs.reduce<{ [ID: string]: string }>((acc, ID) => {
85             acc[ID] = beDeletedModel[ID];
86             return acc;
87         }, {});
89         onMergePreview({
90             beMergedModel: beMergedModelSingle,
91             beDeletedModel: beDeletedModelSingle,
92             updateModel,
93         });
94     };
96     return (
97         <>
98             <ModalTwoHeader title={c('Title').t`Merge contacts`} />
99             <ModalTwoContent>
100                 <Alert className="mb-4">
101                     {c('Description')
102                         .t`Use Drag and Drop to rank merging priority between contacts. Uncheck the contacts you do not want to merge.`}
103                 </Alert>
104                 <Alert className="mb-4" type="warning">
105                     {c('Description')
106                         .t`You can mark for deletion the contacts that you do not want neither to merge nor to keep. Deletion will only take place after the merge button is clicked`}
107                 </Alert>
108                 <MergeTable
109                     onSortEnd={handleSortEnd}
110                     contacts={orderedContacts}
111                     isChecked={isChecked}
112                     beDeleted={beDeleted}
113                     onClickCheckbox={handleToggleCheck}
114                     onClickDetails={onMergeDetails}
115                     onToggleDelete={handleToggleDelete}
116                     onClickPreview={handlePreview}
117                 />
118             </ModalTwoContent>
119             <ModalTwoFooter>
120                 <Button onClick={onClose} data-testid="merge-model-cancel-button">{c('Action').t`Cancel`}</Button>
121                 {isDeleteOnly ? (
122                     <Button color="norm" onClick={onSubmit}>{c('Action').t`Continue`}</Button>
123                 ) : (
124                     <Button
125                         color="norm"
126                         disabled={!totalBeMerged}
127                         onClick={onSubmit}
128                         data-testid="merge-model-merge-button"
129                     >
130                         {c('Action').t`Merge`}
131                     </Button>
132                 )}
133             </ModalTwoFooter>
134         </>
135     );
138 export default ContactMergeTableContent;