fix(frontend): apply feedback updates

This commit is contained in:
yassinedorbozgithub 2025-02-04 11:30:44 +01:00
parent 0b19f45b59
commit 658bfbc924
10 changed files with 80 additions and 70 deletions

View File

@ -43,7 +43,9 @@
"account_update_success": "Account has been updated successfully",
"account_disabled": "Your account has been either disabled or is pending confirmation.",
"success_invitation_sent": "Invitation to join has been successfully sent.",
"item_delete_confirm": "Are you sure you want to delete this item?",
"item_delete_confirm": "Are you sure you want to delete this {{0}} item?",
"items_delete_confirm": "Are you sure you want to delete those {{0}} selected items?",
"selected": "selected",
"item_delete_success": "Item has been deleted successfully",
"success_save": "Changes has been saved!",
"no_result_found": "No result found",

View File

@ -43,7 +43,8 @@
"account_update_success": "Le compte a été mis à jour avec succès",
"account_disabled": "Votre compte a été désactivé ou est en attente de confirmation.",
"success_invitation_sent": "L'invitation a été envoyée avec succès.",
"item_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément?",
"item_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément {{0}}?",
"items_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer ces {{0}} éléments sélectionnés?",
"item_delete_success": "L'élément a été supprimé avec succès",
"success_save": "Les modifications ont été enregistrées!",
"no_result_found": "Aucun résultat trouvé",

View File

@ -6,7 +6,6 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import { Button, Grid } from "@mui/material";
@ -22,7 +21,7 @@ export const DialogFormButtons = <T,>({
return (
<Grid
p="5px 15px"
p="0.3rem 1rem"
width="100%"
display="flex"
justifyContent="space-between"

View File

@ -19,17 +19,14 @@ export const FormDialog = <T,>({
onSubmit,
...rest
}: FormDialogProps<T>) => {
const handleClose = () => rest.onClose?.({}, "backdropClick");
return (
<Dialog fullWidth {...rest}>
<DialogTitle onClose={() => rest.onClose?.({}, "backdropClick")}>
{title}
</DialogTitle>
<DialogTitle onClose={handleClose}>{title}</DialogTitle>
<DialogContent>{children}</DialogContent>
<DialogActions style={{ padding: "8px" }}>
<DialogFormButtons
onCancel={() => rest.onClose?.({}, "backdropClick")}
onSubmit={onSubmit}
/>
<DialogActions style={{ padding: "0.5rem" }}>
<DialogFormButtons onCancel={handleClose} onSubmit={onSubmit} />
</DialogActions>
</Dialog>
);

View File

@ -6,9 +6,16 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { Button, Dialog, DialogActions, DialogContent } from "@mui/material";
import {
Button,
Dialog,
DialogActions,
DialogContent,
Grid,
} from "@mui/material";
import { FC, ReactNode } from "react";
import { useTranslate } from "@/hooks/useTranslate";
import { ConfirmOptions, DialogProps } from "@/types/common/dialogs.types";
import { DialogTitle } from "../DialogTitle";
@ -23,6 +30,7 @@ export interface ConfirmDialogProps
extends DialogProps<ConfirmDialogPayload, boolean> {}
export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
const { t } = useTranslate();
const cancelButtonProps = useDialogLoadingButton(() => rest.onClose(false));
const okButtonProps = useDialogLoadingButton(() => rest.onClose(true));
@ -34,22 +42,24 @@ export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
onClose={() => rest.onClose(false)}
>
<DialogTitle onClose={() => rest.onClose(false)}>
{payload.title}
{payload.title || t("title.warning")}
</DialogTitle>
<DialogContent>{payload.msg}</DialogContent>
<DialogActions>
<Button
color={payload.severity || "error"}
variant="contained"
disabled={!open}
autoFocus
{...okButtonProps}
>
{payload.okText}
</Button>
<Button variant="outlined" disabled={!open} {...cancelButtonProps}>
{payload.cancelText}
</Button>
<Grid p="0.3rem 1rem" gap="0.5rem" display="flex">
<Button
color={payload.severity || "error"}
variant="contained"
disabled={!open}
autoFocus
{...okButtonProps}
>
{payload.okText || t("label.yes")}
</Button>
<Button variant="outlined" disabled={!open} {...cancelButtonProps}>
{payload.cancelText || t("label.no")}
</Button>
</Grid>
</DialogActions>
</Dialog>
);

View File

@ -11,16 +11,30 @@ import { Grid, Typography } from "@mui/material";
import { useTranslate } from "@/hooks/useTranslate";
export const ConfirmDialogBody = () => {
export const ConfirmDialogBody = ({
mode = "click",
itemsNumber = 1,
}: {
mode?: "selection" | "click";
itemsNumber?: number;
}) => {
const { t } = useTranslate();
const DialogBodyText =
itemsNumber === 1
? t("message.item_delete_confirm", {
"0": mode === "click" ? "" : t("message.selected"),
})
: t("message.items_delete_confirm", {
"0": itemsNumber.toString(),
});
return (
<Grid container gap={1}>
<Grid item height="28px">
<ErrorIcon sx={{ fontSize: "28px" }} color="error" />
<Grid item height="1.75rem">
<ErrorIcon sx={{ fontSize: "1.75rem" }} color="error" />
</Grid>
<Grid item alignSelf="center">
<Typography>{t("message.item_delete_confirm")}</Typography>
<Typography>{DialogBodyText}</Typography>
</Grid>
</Grid>
);

View File

@ -21,8 +21,8 @@ import { ComponentFormProps } from "@/types/common/dialogs.types";
export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
data,
FormWrapper = React.Fragment,
FormWrapperProps,
Wrapper = React.Fragment,
WrapperProps,
...rest
}) => {
const { t } = useTranslate();
@ -84,10 +84,10 @@ export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
}, [data, reset]);
return (
<FormWrapper
{...FormWrapperProps}
<Wrapper
open={!!WrapperProps?.open}
onSubmit={submitAsync}
open={!!FormWrapperProps?.open}
{...WrapperProps}
>
<form onSubmit={submitAsync}>
<ContentContainer>
@ -102,7 +102,7 @@ export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
</ContentItem>
</ContentContainer>
</form>
</FormWrapper>
</Wrapper>
);
};

View File

@ -27,8 +27,8 @@ export const CategoryFormDialog: FC<ComponentFormDialogProps<ICategory>> = ({
onSuccess={() => {
rest.onClose(true);
}}
FormWrapper={FormDialog}
FormWrapperProps={{
Wrapper={FormDialog}
WrapperProps={{
title: payload ? t("title.edit_category") : t("title.new_category"),
...rest,
}}

View File

@ -1,11 +1,12 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
import FolderIcon from "@mui/icons-material/Folder";
@ -24,7 +25,6 @@ import { DataGrid } from "@/app-components/tables/DataGrid";
import { useDelete } from "@/hooks/crud/useDelete";
import { useDeleteMany } from "@/hooks/crud/useDeleteMany";
import { useFind } from "@/hooks/crud/useFind";
import { useDialog } from "@/hooks/useDialog";
import { useDialogs } from "@/hooks/useDialogs";
import { useHasPermission } from "@/hooks/useHasPermission";
import { useSearch } from "@/hooks/useSearch";
@ -42,7 +42,6 @@ import { CategoryFormDialog } from "./CategoryFormDialog";
export const Categories = () => {
const { t } = useTranslate();
const { toast } = useToast();
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const { onSearch, searchPayload } = useSearch<ICategory>({
$iLike: ["label"],
@ -53,26 +52,20 @@ export const Categories = () => {
params: searchPayload,
},
);
const { mutateAsync: deleteCategory } = useDelete(EntityType.CATEGORY, {
onError: (error) => {
const options = {
onError: (error: Error) => {
toast.error(error.message || t("message.internal_server_error"));
},
onSuccess: () => {
deleteDialogCtl.closeDialog();
setSelectedCategories([]);
toast.success(t("message.item_delete_success"));
},
});
const { mutateAsync: deleteCategories } = useDeleteMany(EntityType.CATEGORY, {
onError: (error) => {
toast.error(error.message || t("message.internal_server_error"));
},
onSuccess: () => {
deleteDialogCtl.closeDialog();
setSelectedCategories([]);
toast.success(t("message.item_delete_success"));
},
});
};
const { mutate: deleteCategory } = useDelete(EntityType.CATEGORY, options);
const { mutate: deleteCategories } = useDeleteMany(
EntityType.CATEGORY,
options,
);
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const actionColumns = useActionColumns<ICategory>(
EntityType.CATEGORY,
@ -87,14 +80,10 @@ export const Categories = () => {
{
label: ActionColumnLabel.Delete,
action: async ({ id }) => {
const isConfirmed = await dialogs.confirm(<ConfirmDialogBody />, {
title: t("title.warning"),
okText: t("label.yes"),
cancelText: t("label.no"),
});
const isConfirmed = await dialogs.confirm(<ConfirmDialogBody />);
if (isConfirmed) {
await deleteCategory(id);
deleteCategory(id);
}
},
requires: [PermissionAction.DELETE],
@ -176,16 +165,14 @@ export const Categories = () => {
color="error"
onClick={async () => {
const isConfirmed = await dialogs.confirm(
<ConfirmDialogBody />,
{
title: t("title.warning"),
okText: t("label.yes"),
cancelText: t("label.no"),
},
<ConfirmDialogBody
mode="selection"
itemsNumber={selectedCategories.length}
/>,
);
if (isConfirmed) {
await deleteCategories(selectedCategories);
deleteCategories(selectedCategories);
}
}}
>

View File

@ -148,8 +148,8 @@ export type ComponentFormProps<T> = {
data: T | null;
onError?: () => void;
onSuccess?: () => void;
FormWrapper?: React.FC<FormDialogProps<T>>;
FormWrapperProps?: Partial<FormDialogProps<T>>;
Wrapper?: React.FC<FormDialogProps<T>>;
WrapperProps?: Partial<FormDialogProps<T>>;
};
export interface FormButtonsProps<T> {