mirror of
https://github.com/hexastack/hexabot
synced 2025-02-22 20:38:32 +00:00
fix(frontend): apply feedback updates
This commit is contained in:
parent
0b19f45b59
commit
658bfbc924
@ -43,7 +43,9 @@
|
|||||||
"account_update_success": "Account has been updated successfully",
|
"account_update_success": "Account has been updated successfully",
|
||||||
"account_disabled": "Your account has been either disabled or is pending confirmation.",
|
"account_disabled": "Your account has been either disabled or is pending confirmation.",
|
||||||
"success_invitation_sent": "Invitation to join has been successfully sent.",
|
"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",
|
"item_delete_success": "Item has been deleted successfully",
|
||||||
"success_save": "Changes has been saved!",
|
"success_save": "Changes has been saved!",
|
||||||
"no_result_found": "No result found",
|
"no_result_found": "No result found",
|
||||||
|
@ -43,7 +43,8 @@
|
|||||||
"account_update_success": "Le compte a été mis à jour avec succès",
|
"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.",
|
"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.",
|
"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",
|
"item_delete_success": "L'élément a été supprimé avec succès",
|
||||||
"success_save": "Les modifications ont été enregistrées!",
|
"success_save": "Les modifications ont été enregistrées!",
|
||||||
"no_result_found": "Aucun résultat trouvé",
|
"no_result_found": "Aucun résultat trouvé",
|
||||||
|
@ -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).
|
* 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 CheckIcon from "@mui/icons-material/Check";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { Button, Grid } from "@mui/material";
|
import { Button, Grid } from "@mui/material";
|
||||||
@ -22,7 +21,7 @@ export const DialogFormButtons = <T,>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
p="5px 15px"
|
p="0.3rem 1rem"
|
||||||
width="100%"
|
width="100%"
|
||||||
display="flex"
|
display="flex"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
|
@ -19,17 +19,14 @@ export const FormDialog = <T,>({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
...rest
|
...rest
|
||||||
}: FormDialogProps<T>) => {
|
}: FormDialogProps<T>) => {
|
||||||
|
const handleClose = () => rest.onClose?.({}, "backdropClick");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog fullWidth {...rest}>
|
<Dialog fullWidth {...rest}>
|
||||||
<DialogTitle onClose={() => rest.onClose?.({}, "backdropClick")}>
|
<DialogTitle onClose={handleClose}>{title}</DialogTitle>
|
||||||
{title}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>{children}</DialogContent>
|
<DialogContent>{children}</DialogContent>
|
||||||
<DialogActions style={{ padding: "8px" }}>
|
<DialogActions style={{ padding: "0.5rem" }}>
|
||||||
<DialogFormButtons
|
<DialogFormButtons onCancel={handleClose} onSubmit={onSubmit} />
|
||||||
onCancel={() => rest.onClose?.({}, "backdropClick")}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
/>
|
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
@ -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).
|
* 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 { FC, ReactNode } from "react";
|
||||||
|
|
||||||
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
import { ConfirmOptions, DialogProps } from "@/types/common/dialogs.types";
|
import { ConfirmOptions, DialogProps } from "@/types/common/dialogs.types";
|
||||||
|
|
||||||
import { DialogTitle } from "../DialogTitle";
|
import { DialogTitle } from "../DialogTitle";
|
||||||
@ -23,6 +30,7 @@ export interface ConfirmDialogProps
|
|||||||
extends DialogProps<ConfirmDialogPayload, boolean> {}
|
extends DialogProps<ConfirmDialogPayload, boolean> {}
|
||||||
|
|
||||||
export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
|
export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
|
||||||
|
const { t } = useTranslate();
|
||||||
const cancelButtonProps = useDialogLoadingButton(() => rest.onClose(false));
|
const cancelButtonProps = useDialogLoadingButton(() => rest.onClose(false));
|
||||||
const okButtonProps = useDialogLoadingButton(() => rest.onClose(true));
|
const okButtonProps = useDialogLoadingButton(() => rest.onClose(true));
|
||||||
|
|
||||||
@ -34,10 +42,11 @@ export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
|
|||||||
onClose={() => rest.onClose(false)}
|
onClose={() => rest.onClose(false)}
|
||||||
>
|
>
|
||||||
<DialogTitle onClose={() => rest.onClose(false)}>
|
<DialogTitle onClose={() => rest.onClose(false)}>
|
||||||
{payload.title}
|
{payload.title || t("title.warning")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>{payload.msg}</DialogContent>
|
<DialogContent>{payload.msg}</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
<Grid p="0.3rem 1rem" gap="0.5rem" display="flex">
|
||||||
<Button
|
<Button
|
||||||
color={payload.severity || "error"}
|
color={payload.severity || "error"}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -45,11 +54,12 @@ export const ConfirmDialog: FC<ConfirmDialogProps> = ({ payload, ...rest }) => {
|
|||||||
autoFocus
|
autoFocus
|
||||||
{...okButtonProps}
|
{...okButtonProps}
|
||||||
>
|
>
|
||||||
{payload.okText}
|
{payload.okText || t("label.yes")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outlined" disabled={!open} {...cancelButtonProps}>
|
<Button variant="outlined" disabled={!open} {...cancelButtonProps}>
|
||||||
{payload.cancelText}
|
{payload.cancelText || t("label.no")}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Grid>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
@ -11,16 +11,30 @@ import { Grid, Typography } from "@mui/material";
|
|||||||
|
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
|
||||||
export const ConfirmDialogBody = () => {
|
export const ConfirmDialogBody = ({
|
||||||
|
mode = "click",
|
||||||
|
itemsNumber = 1,
|
||||||
|
}: {
|
||||||
|
mode?: "selection" | "click";
|
||||||
|
itemsNumber?: number;
|
||||||
|
}) => {
|
||||||
const { t } = useTranslate();
|
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 (
|
return (
|
||||||
<Grid container gap={1}>
|
<Grid container gap={1}>
|
||||||
<Grid item height="28px">
|
<Grid item height="1.75rem">
|
||||||
<ErrorIcon sx={{ fontSize: "28px" }} color="error" />
|
<ErrorIcon sx={{ fontSize: "1.75rem" }} color="error" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item alignSelf="center">
|
<Grid item alignSelf="center">
|
||||||
<Typography>{t("message.item_delete_confirm")}</Typography>
|
<Typography>{DialogBodyText}</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -21,8 +21,8 @@ import { ComponentFormProps } from "@/types/common/dialogs.types";
|
|||||||
|
|
||||||
export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
|
export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
|
||||||
data,
|
data,
|
||||||
FormWrapper = React.Fragment,
|
Wrapper = React.Fragment,
|
||||||
FormWrapperProps,
|
WrapperProps,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslate();
|
||||||
@ -84,10 +84,10 @@ export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
|
|||||||
}, [data, reset]);
|
}, [data, reset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormWrapper
|
<Wrapper
|
||||||
{...FormWrapperProps}
|
open={!!WrapperProps?.open}
|
||||||
onSubmit={submitAsync}
|
onSubmit={submitAsync}
|
||||||
open={!!FormWrapperProps?.open}
|
{...WrapperProps}
|
||||||
>
|
>
|
||||||
<form onSubmit={submitAsync}>
|
<form onSubmit={submitAsync}>
|
||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
@ -102,7 +102,7 @@ export const CategoryForm: FC<ComponentFormProps<ICategory>> = ({
|
|||||||
</ContentItem>
|
</ContentItem>
|
||||||
</ContentContainer>
|
</ContentContainer>
|
||||||
</form>
|
</form>
|
||||||
</FormWrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ export const CategoryFormDialog: FC<ComponentFormDialogProps<ICategory>> = ({
|
|||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
rest.onClose(true);
|
rest.onClose(true);
|
||||||
}}
|
}}
|
||||||
FormWrapper={FormDialog}
|
Wrapper={FormDialog}
|
||||||
FormWrapperProps={{
|
WrapperProps={{
|
||||||
title: payload ? t("title.edit_category") : t("title.new_category"),
|
title: payload ? t("title.edit_category") : t("title.new_category"),
|
||||||
...rest,
|
...rest,
|
||||||
}}
|
}}
|
||||||
|
@ -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:
|
* 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.
|
* 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).
|
* 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 AddIcon from "@mui/icons-material/Add";
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import FolderIcon from "@mui/icons-material/Folder";
|
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 { useDelete } from "@/hooks/crud/useDelete";
|
||||||
import { useDeleteMany } from "@/hooks/crud/useDeleteMany";
|
import { useDeleteMany } from "@/hooks/crud/useDeleteMany";
|
||||||
import { useFind } from "@/hooks/crud/useFind";
|
import { useFind } from "@/hooks/crud/useFind";
|
||||||
import { useDialog } from "@/hooks/useDialog";
|
|
||||||
import { useDialogs } from "@/hooks/useDialogs";
|
import { useDialogs } from "@/hooks/useDialogs";
|
||||||
import { useHasPermission } from "@/hooks/useHasPermission";
|
import { useHasPermission } from "@/hooks/useHasPermission";
|
||||||
import { useSearch } from "@/hooks/useSearch";
|
import { useSearch } from "@/hooks/useSearch";
|
||||||
@ -42,7 +42,6 @@ import { CategoryFormDialog } from "./CategoryFormDialog";
|
|||||||
export const Categories = () => {
|
export const Categories = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslate();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const deleteDialogCtl = useDialog<string>(false);
|
|
||||||
const hasPermission = useHasPermission();
|
const hasPermission = useHasPermission();
|
||||||
const { onSearch, searchPayload } = useSearch<ICategory>({
|
const { onSearch, searchPayload } = useSearch<ICategory>({
|
||||||
$iLike: ["label"],
|
$iLike: ["label"],
|
||||||
@ -53,26 +52,20 @@ export const Categories = () => {
|
|||||||
params: searchPayload,
|
params: searchPayload,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { mutateAsync: deleteCategory } = useDelete(EntityType.CATEGORY, {
|
const options = {
|
||||||
onError: (error) => {
|
onError: (error: Error) => {
|
||||||
toast.error(error.message || t("message.internal_server_error"));
|
toast.error(error.message || t("message.internal_server_error"));
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
deleteDialogCtl.closeDialog();
|
|
||||||
setSelectedCategories([]);
|
setSelectedCategories([]);
|
||||||
toast.success(t("message.item_delete_success"));
|
toast.success(t("message.item_delete_success"));
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
const { mutateAsync: deleteCategories } = useDeleteMany(EntityType.CATEGORY, {
|
const { mutate: deleteCategory } = useDelete(EntityType.CATEGORY, options);
|
||||||
onError: (error) => {
|
const { mutate: deleteCategories } = useDeleteMany(
|
||||||
toast.error(error.message || t("message.internal_server_error"));
|
EntityType.CATEGORY,
|
||||||
},
|
options,
|
||||||
onSuccess: () => {
|
);
|
||||||
deleteDialogCtl.closeDialog();
|
|
||||||
setSelectedCategories([]);
|
|
||||||
toast.success(t("message.item_delete_success"));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
||||||
const actionColumns = useActionColumns<ICategory>(
|
const actionColumns = useActionColumns<ICategory>(
|
||||||
EntityType.CATEGORY,
|
EntityType.CATEGORY,
|
||||||
@ -87,14 +80,10 @@ export const Categories = () => {
|
|||||||
{
|
{
|
||||||
label: ActionColumnLabel.Delete,
|
label: ActionColumnLabel.Delete,
|
||||||
action: async ({ id }) => {
|
action: async ({ id }) => {
|
||||||
const isConfirmed = await dialogs.confirm(<ConfirmDialogBody />, {
|
const isConfirmed = await dialogs.confirm(<ConfirmDialogBody />);
|
||||||
title: t("title.warning"),
|
|
||||||
okText: t("label.yes"),
|
|
||||||
cancelText: t("label.no"),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
await deleteCategory(id);
|
deleteCategory(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
requires: [PermissionAction.DELETE],
|
requires: [PermissionAction.DELETE],
|
||||||
@ -176,16 +165,14 @@ export const Categories = () => {
|
|||||||
color="error"
|
color="error"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const isConfirmed = await dialogs.confirm(
|
const isConfirmed = await dialogs.confirm(
|
||||||
<ConfirmDialogBody />,
|
<ConfirmDialogBody
|
||||||
{
|
mode="selection"
|
||||||
title: t("title.warning"),
|
itemsNumber={selectedCategories.length}
|
||||||
okText: t("label.yes"),
|
/>,
|
||||||
cancelText: t("label.no"),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
await deleteCategories(selectedCategories);
|
deleteCategories(selectedCategories);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -148,8 +148,8 @@ export type ComponentFormProps<T> = {
|
|||||||
data: T | null;
|
data: T | null;
|
||||||
onError?: () => void;
|
onError?: () => void;
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
FormWrapper?: React.FC<FormDialogProps<T>>;
|
Wrapper?: React.FC<FormDialogProps<T>>;
|
||||||
FormWrapperProps?: Partial<FormDialogProps<T>>;
|
WrapperProps?: Partial<FormDialogProps<T>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface FormButtonsProps<T> {
|
export interface FormButtonsProps<T> {
|
||||||
|
Loading…
Reference in New Issue
Block a user