blob: aa9f01c903367ced78f09f6c289696d460503d5a [file] [log] [blame]
idillonef9ab812022-11-18 13:46:24 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import { Box, Button, List, ListItem, ListItemIcon, Stack, SvgIconProps, Typography } from '@mui/material';
19import Dialog from '@mui/material/Dialog';
20import DialogActions from '@mui/material/DialogActions';
21import DialogContent from '@mui/material/DialogContent';
22import DialogTitle from '@mui/material/DialogTitle';
23import { ComponentType, ReactNode, useCallback, useMemo, useState } from 'react';
24import { useTranslation } from 'react-i18next';
25
26interface DialogHandler {
27 props: {
28 open: boolean;
29 onClose: () => void;
30 };
31 openDialog: () => void;
32}
33
34export const useDialogHandler = (): DialogHandler => {
35 const [open, setOpen] = useState(false);
36
37 const onClose = useCallback(() => {
38 setOpen(false);
39 }, []);
40
41 const openDialog = useCallback(() => {
42 setOpen(true);
43 }, []);
44
45 return useMemo(
46 () => ({
47 props: { open, onClose },
48 openDialog,
49 }),
50 [open, onClose, openDialog]
51 );
52};
53
54interface BaseDialogProps {
55 open: boolean;
56 onClose: () => void;
57 icon?: ReactNode;
58 title: string;
59 content: ReactNode;
60 actions: ReactNode;
61}
62
63export const BaseDialog = ({ open, onClose, icon, title, content, actions }: BaseDialogProps) => {
64 return (
65 <Dialog open={open} onClose={onClose}>
66 <DialogTitle>
67 <Stack direction="row" alignItems="center" spacing="16px">
68 {icon && (
69 <Box height="80px" width="80px">
70 {icon}
71 </Box>
72 )}
73 <Box>
74 <Typography variant="h2">{title}</Typography>
75 </Box>
76 </Stack>
77 </DialogTitle>
78 <DialogContent>{content}</DialogContent>
79 <DialogActions>{actions}</DialogActions>
80 </Dialog>
81 );
82};
83
84type InfosDialogProps = Omit<BaseDialogProps, 'actions'>;
85
86export const InfosDialog = (props: InfosDialogProps) => {
87 const { t } = useTranslation();
88 return (
89 <BaseDialog
90 {...props}
91 actions={
92 <Button onClick={props.onClose} autoFocus>
93 {t('dialog_close')}
94 </Button>
95 }
96 />
97 );
98};
99
100interface ConfirmationDialogProps extends Omit<BaseDialogProps, 'actions'> {
101 onConfirm: () => void;
102 confirmButtonText: string;
idillon22ed8192022-11-29 12:17:04 -0500103 content: string;
idillonef9ab812022-11-18 13:46:24 -0500104}
105
106export const ConfirmationDialog = ({ onConfirm, confirmButtonText, ...props }: ConfirmationDialogProps) => {
107 const { t } = useTranslation();
108 props.title = props.title || t('dialog_confirm_title_default');
109
110 return (
111 <BaseDialog
112 {...props}
idillon22ed8192022-11-29 12:17:04 -0500113 content={<Typography variant="body1">{props.content}</Typography>}
idillonef9ab812022-11-18 13:46:24 -0500114 actions={
115 <>
116 <Button onClick={onConfirm}>{confirmButtonText}</Button>
117 <Button onClick={props.onClose}>{t('dialog_cancel')}</Button>
118 </>
119 }
120 />
121 );
122};
123
124interface DialogContentListItem {
125 Icon?: ComponentType<SvgIconProps>;
126 label?: string;
127 value: ReactNode;
128}
129
130interface DialogContentListProps {
131 title?: string;
132 items: DialogContentListItem[];
133}
134
135export const DialogContentList = ({ title, items }: DialogContentListProps) => {
136 return (
137 <List subheader={<Typography variant="h3">{title}</Typography>}>
138 {items.map(({ Icon, label, value }, index) => (
139 <ListItem key={index}>
140 {Icon && (
141 <ListItemIcon>
142 <Icon />
143 </ListItemIcon>
144 )}
145 <Stack direction="row" alignItems="center" spacing="24px">
146 {label && (
147 <Stack direction="row" width="100px" justifyContent="end">
148 <Typography variant="body2" color="#a0a0a0">
149 {label}
150 </Typography>
151 </Stack>
152 )}
153 <Box>
154 <Typography variant="body2" sx={{ fontWeight: 'bold' }}>
155 {value}
156 </Typography>
157 </Box>
158 </Stack>
159 </ListItem>
160 ))}
161 </List>
162 );
163};