blob: aa9f01c903367ced78f09f6c289696d460503d5a [file] [log] [blame]
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
import { Box, Button, List, ListItem, ListItemIcon, Stack, SvgIconProps, Typography } from '@mui/material';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { ComponentType, ReactNode, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
interface DialogHandler {
props: {
open: boolean;
onClose: () => void;
};
openDialog: () => void;
}
export const useDialogHandler = (): DialogHandler => {
const [open, setOpen] = useState(false);
const onClose = useCallback(() => {
setOpen(false);
}, []);
const openDialog = useCallback(() => {
setOpen(true);
}, []);
return useMemo(
() => ({
props: { open, onClose },
openDialog,
}),
[open, onClose, openDialog]
);
};
interface BaseDialogProps {
open: boolean;
onClose: () => void;
icon?: ReactNode;
title: string;
content: ReactNode;
actions: ReactNode;
}
export const BaseDialog = ({ open, onClose, icon, title, content, actions }: BaseDialogProps) => {
return (
<Dialog open={open} onClose={onClose}>
<DialogTitle>
<Stack direction="row" alignItems="center" spacing="16px">
{icon && (
<Box height="80px" width="80px">
{icon}
</Box>
)}
<Box>
<Typography variant="h2">{title}</Typography>
</Box>
</Stack>
</DialogTitle>
<DialogContent>{content}</DialogContent>
<DialogActions>{actions}</DialogActions>
</Dialog>
);
};
type InfosDialogProps = Omit<BaseDialogProps, 'actions'>;
export const InfosDialog = (props: InfosDialogProps) => {
const { t } = useTranslation();
return (
<BaseDialog
{...props}
actions={
<Button onClick={props.onClose} autoFocus>
{t('dialog_close')}
</Button>
}
/>
);
};
interface ConfirmationDialogProps extends Omit<BaseDialogProps, 'actions'> {
onConfirm: () => void;
confirmButtonText: string;
content: string;
}
export const ConfirmationDialog = ({ onConfirm, confirmButtonText, ...props }: ConfirmationDialogProps) => {
const { t } = useTranslation();
props.title = props.title || t('dialog_confirm_title_default');
return (
<BaseDialog
{...props}
content={<Typography variant="body1">{props.content}</Typography>}
actions={
<>
<Button onClick={onConfirm}>{confirmButtonText}</Button>
<Button onClick={props.onClose}>{t('dialog_cancel')}</Button>
</>
}
/>
);
};
interface DialogContentListItem {
Icon?: ComponentType<SvgIconProps>;
label?: string;
value: ReactNode;
}
interface DialogContentListProps {
title?: string;
items: DialogContentListItem[];
}
export const DialogContentList = ({ title, items }: DialogContentListProps) => {
return (
<List subheader={<Typography variant="h3">{title}</Typography>}>
{items.map(({ Icon, label, value }, index) => (
<ListItem key={index}>
{Icon && (
<ListItemIcon>
<Icon />
</ListItemIcon>
)}
<Stack direction="row" alignItems="center" spacing="24px">
{label && (
<Stack direction="row" width="100px" justifyContent="end">
<Typography variant="body2" color="#a0a0a0">
{label}
</Typography>
</Stack>
)}
<Box>
<Typography variant="body2" sx={{ fontWeight: 'bold' }}>
{value}
</Typography>
</Box>
</Stack>
</ListItem>
))}
</List>
);
};