fix: update modals logic

This commit is contained in:
yassinedorbozgithub
2025-01-25 14:30:09 +01:00
parent c124972bbd
commit e601618abd
37 changed files with 334 additions and 273 deletions

View File

@@ -44,6 +44,7 @@
"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?",
"items_delete_confirm": "Are you sure you want to delete those {{0}} selected items?",
"item_delete_success": "Item has been deleted successfully",
"success_save": "Changes has been saved!",
"no_result_found": "No result found",

View File

@@ -44,6 +44,7 @@
"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?",
"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

@@ -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 CancelOutlinedIcon from "@mui/icons-material/CancelOutlined";
import DeleteOutlineOutlinedIcon from "@mui/icons-material/DeleteOutlineOutlined";
import FileOpenIcon from "@mui/icons-material/FileOpen";
@@ -84,7 +85,7 @@ const AttachmentThumbnail: FC<AttachmentThumbnailProps> = ({
});
const { toast } = useToast();
const { t } = useTranslate();
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
if (!attachment) {
return t("message.attachment_not_found") + id;
@@ -141,7 +142,7 @@ const AttachmentThumbnail: FC<AttachmentThumbnailProps> = ({
variant="contained"
startIcon={<DeleteOutlineOutlinedIcon />}
onClick={(e) => {
deleteDialogCtl.openDialog([attachment.id]);
deleteDialogCtl.openDialog(attachment.id);
e.preventDefault();
e.stopPropagation();
}}

View File

@@ -94,7 +94,7 @@ const AttachmentUploader: FC<FileUploadProps> = ({
onUploadComplete && onUploadComplete();
},
});
const libraryDialogCtl = useDialog<IAttachment | null | undefined>(false);
const libraryDialogCtl = useDialog<IAttachment | null>(false);
const stopDefaults = (e: DragEvent) => {
e.stopPropagation();
e.preventDefault();
@@ -140,7 +140,9 @@ const AttachmentUploader: FC<FileUploadProps> = ({
<Grid>
<AttachmentDialog
{...getDisplayDialogs(libraryDialogCtl)}
callback={onChange}
callback={async (data) => {
if (!Array.isArray(data)) onChange?.(data);
}}
accept={accept}
/>
<Grid container>

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 ErrorIcon from "@mui/icons-material/Error";
import {
Button,
@@ -24,10 +25,11 @@ import { useTranslate } from "@/hooks/useTranslate";
import { EntityType } from "@/services/types";
import { IEntityMapTypes } from "@/types/base.types";
export type DeleteDialogProps<T = string> = DialogControl<T>;
export const DeleteDialog = <T extends any = string>({
export type DeleteDialogProps<T extends string = string> = DialogControl<T>;
export const DeleteDialog = <T extends string = string>({
open,
closeDialog: closeFunction,
datum,
data: ids,
callback,
entity = EntityType.ATTACHMENT,
@@ -40,6 +42,9 @@ export const DeleteDialog = <T extends any = string>({
onDeleteSuccess?: (data?: unknown) => void;
}) => {
const { t } = useTranslate();
const getItemsFromData = (data: unknown) => (Array.isArray(data) ? data : []);
const hasMultipleItems = (data: unknown): boolean =>
getItemsFromData(data).length > 1;
const onSuccess = (data: unknown) => {
setData?.(undefined);
onDeleteSuccess(data);
@@ -62,7 +67,16 @@ export const DeleteDialog = <T extends any = string>({
<ErrorIcon sx={{ fontSize: "28px" }} color="error" />
</Grid>
<Grid item alignSelf="center">
<Typography>{t("message.item_delete_confirm")}</Typography>
<Typography>
{t(
`${
hasMultipleItems(ids)
? "message.items_delete_confirm"
: "message.item_delete_confirm"
}`,
{ "0": getItemsFromData(ids).length.toString() },
)}
</Typography>
</Grid>
</Grid>
</DialogContent>
@@ -73,16 +87,10 @@ export const DeleteDialog = <T extends any = string>({
onClick={async () => {
if (callback) {
callback(ids);
} else {
if (!Array.isArray(ids)) {
throw new Error("IDs need to be an Array");
} else if (ids.length === 0) {
throw new Error("IDs cannot be empty");
} else if (ids.length === 1) {
await deleteOne(ids[0]);
} else if (ids.length > 1) {
await deleteMany(ids);
}
} else if (Array.isArray(ids)) {
await deleteMany(ids);
} else if (datum) {
await deleteOne(datum);
}
}}
autoFocus

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 {
Button,
Dialog,
@@ -22,7 +23,8 @@ import { DialogControl } from "@/hooks/useDialog";
import { useTranslate } from "@/hooks/useTranslate";
import { ICategory } from "@/types/category.types";
export interface MoveDialogProps extends DialogControl<string> {
export interface MoveDialogProps
extends Omit<DialogControl<string>, "callback"> {
categories: ICategory[];
callback?: (newCategoryId?: string) => Promise<void>;
openDialog: (data?: string) => void;

View File

@@ -1,15 +1,16 @@
/*
* 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 { faBars } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import { Grid, Paper, Button, Box, debounce } from "@mui/material";
import React, { useRef, useState } from "react";
import { Box, Button, debounce, Grid, Paper } from "@mui/material";
import { useRef, useState } from "react";
import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog";
import { NoDataOverlay } from "@/app-components/tables/NoDataOverlay";
@@ -165,7 +166,7 @@ export const Menu = () => {
open={deleteDialogOpened}
openDialog={() => setDeleteDialogOpened(true)}
closeDialog={() => setDeleteDialogOpened(false)}
callback={() => {
callback={async () => {
if (deleteRowId) {
deleteMenu(deleteRowId);
}

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 { Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
@@ -27,7 +28,7 @@ export type CategoryDialogProps = DialogControlProps<ICategory>;
export const CategoryDialog: FC<CategoryDialogProps> = ({
open,
data,
datum: category,
closeDialog,
...rest
}) => {
@@ -57,7 +58,7 @@ export const CategoryDialog: FC<CategoryDialogProps> = ({
formState: { errors },
handleSubmit,
} = useForm<ICategoryAttributes>({
defaultValues: { label: data?.label || "" },
defaultValues: { label: category?.label || "" },
});
const validationRules = {
label: {
@@ -65,8 +66,8 @@ export const CategoryDialog: FC<CategoryDialogProps> = ({
},
};
const onSubmitForm = async (params: ICategoryAttributes) => {
if (data) {
updateCategory({ id: data.id, params });
if (category) {
updateCategory({ id: category.id, params });
} else {
createCategory(params);
}
@@ -77,20 +78,20 @@ export const CategoryDialog: FC<CategoryDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (category) {
reset({
label: data.label,
label: category.label,
});
} else {
reset();
}
}, [data, reset]);
}, [category, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_category") : t("title.new_category")}
{category ? t("title.edit_category") : t("title.new_category")}
</DialogTitle>
<DialogContent>
<ContentContainer>

View File

@@ -1,16 +1,17 @@
/*
* 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";
import { Button, Grid, Paper } from "@mui/material";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { GridColDef } from "@mui/x-data-grid";
import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog";
import { FilterTextfield } from "@/app-components/inputs/FilterTextfield";
@@ -40,7 +41,7 @@ export const Categories = () => {
const { toast } = useToast();
const addDialogCtl = useDialog<ICategory>(false);
const editDialogCtl = useDialog<ICategory>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const { onSearch, searchPayload } = useSearch<ICategory>({
$iLike: ["label"],
@@ -61,7 +62,7 @@ export const Categories = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],
@@ -101,8 +102,6 @@ export const Categories = () => {
},
actionColumns,
];
const handleSelectionChange = (selection: GridRowSelectionModel) =>
deleteDialogCtl.setData?.(selection as string[]);
return (
<Grid container gap={3} flexDirection="column">
@@ -150,7 +149,7 @@ export const Categories = () => {
variant="contained"
color="error"
onClick={() => deleteDialogCtl.openDialog()}
disabled={!deleteDialogCtl.data?.length}
disabled={!deleteDialogCtl.data?.length || deleteDialogCtl.open}
>
{t("button.delete")}
</Button>
@@ -165,7 +164,10 @@ export const Categories = () => {
columns={columns}
{...dataGridProps}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
rowSelectionModel={deleteDialogCtl.data || []}
onRowSelectionModelChange={(rowSelectionModel) =>
deleteDialogCtl.setData?.(rowSelectionModel as string[])
}
/>
</Grid>
</Paper>

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 { Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
@@ -29,7 +30,7 @@ import {
export type ContentTypeDialogProps = DialogControlProps<IContentType>;
export const ContentTypeDialog: FC<ContentTypeDialogProps> = ({
open,
data,
datum: contentType,
closeDialog,
}) => {
const { toast } = useToast();
@@ -40,7 +41,7 @@ export const ContentTypeDialog: FC<ContentTypeDialogProps> = ({
reset,
formState: { errors },
} = useForm<IContentTypeAttributes>({
defaultValues: { name: data?.name || "" },
defaultValues: { name: contentType?.name || "" },
});
const CloseAndReset = () => {
closeDialog();
@@ -76,9 +77,9 @@ export const ContentTypeDialog: FC<ContentTypeDialogProps> = ({
},
};
const onSubmitForm = async (params: IContentTypeAttributes) => {
if (data) {
if (contentType) {
updateContentType({
id: data.id,
id: contentType.id,
params,
});
} else {
@@ -91,20 +92,22 @@ export const ContentTypeDialog: FC<ContentTypeDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (contentType) {
reset({
name: data.name,
name: contentType.name,
});
} else {
reset();
}
}, [data, reset]);
}, [contentType, reset]);
return (
<Dialog open={open} fullWidth onClose={CloseAndReset}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={CloseAndReset}>
{data ? t("title.edit_content_type") : t("title.new_content_type")}
{contentType
? t("title.edit_content_type")
: t("title.new_content_type")}
</DialogTitle>
<DialogContent>
<ContentContainer>

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 {
Button,
@@ -20,9 +21,9 @@ import { useFieldArray, useForm } from "react-hook-form";
import DialogButtons from "@/app-components/buttons/DialogButtons";
import {
DialogTitle,
ContentContainer,
ContentItem,
DialogTitle,
} from "@/app-components/dialogs";
import { Input } from "@/app-components/inputs/Input";
import { useGet } from "@/hooks/crud/useGet";
@@ -39,9 +40,9 @@ import { FIELDS_FORM_DEFAULT_VALUES, READ_ONLY_FIELDS } from "./constants";
export type EditContentTypeDialogFieldsProps = DialogControlProps<IContentType>;
export const EditContentTypeFieldsDialog = ({
data: contentType,
closeDialog,
open,
datum: contentType,
closeDialog,
}: EditContentTypeDialogFieldsProps) => {
const { t } = useTranslate();
const { isLoading, data, refetch } = useGet(contentType?.id || "", {
@@ -113,10 +114,6 @@ export const EditContentTypeFieldsDialog = ({
}
}, [data, reset]);
function handleClose() {
closeDialog();
}
const { mutateAsync: updateContentType } = useUpdate(
EntityType.CONTENT_TYPE,
{
@@ -135,9 +132,9 @@ export const EditContentTypeFieldsDialog = ({
fullWidth
maxWidth="xl"
sx={{ width: "fit-content", mx: "auto", minWidth: "600px" }}
onClose={handleClose}
onClose={closeDialog}
>
<DialogTitle onClose={handleClose}>
<DialogTitle onClose={closeDialog}>
{t("title.manage_fields")}
</DialogTitle>
<form
@@ -150,7 +147,7 @@ export const EditContentTypeFieldsDialog = ({
fields,
},
});
handleClose();
closeDialog();
})}
>
<DialogContent>

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 { faAlignLeft } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import { Button, Grid, Paper } from "@mui/material";
@@ -41,7 +42,7 @@ export const ContentTypes = () => {
// Dialog Controls
const addDialogCtl = useDialog<IContentType>(false);
const editDialogCtl = useDialog<IContentType>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
// data fetching
const { onSearch, searchPayload } = useSearch<IContentType>({
$iLike: ["name"],
@@ -67,7 +68,7 @@ export const ContentTypes = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],

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 LinkIcon from "@mui/icons-material/Link";
import {
Dialog,
@@ -146,11 +145,11 @@ export type ContentDialogProps = DialogControlProps<{
}>;
export const ContentDialog: FC<ContentDialogProps> = ({
open,
data,
datum,
closeDialog,
...rest
}) => {
const { content, contentType } = data || {
const { content, contentType } = datum || {
content: undefined,
contentType: undefined,
};

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 CloseIcon from "@mui/icons-material/Close";
import { Button, Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useState } from "react";
@@ -29,7 +28,7 @@ export type ContentImportDialogProps = DialogControlProps<{
export const ContentImportDialog: FC<ContentImportDialogProps> = ({
open,
data,
datum,
closeDialog,
...rest
}) => {
@@ -38,10 +37,10 @@ export const ContentImportDialog: FC<ContentImportDialogProps> = ({
const { toast } = useToast();
const { apiClient } = useApiClient();
const { refetch, isFetching } = useQuery(
["importContent", data?.contentType?.id, attachmentId],
["importContent", datum?.contentType?.id, attachmentId],
async () => {
if (data?.contentType?.id && attachmentId) {
await apiClient.importContent(data.contentType.id, attachmentId);
if (datum?.contentType?.id && attachmentId) {
await apiClient.importContent(datum.contentType.id, attachmentId);
}
},
{
@@ -63,7 +62,7 @@ export const ContentImportDialog: FC<ContentImportDialogProps> = ({
setAttachmentId(null);
};
const handleImportClick = () => {
if (attachmentId && data?.contentType?.id) {
if (attachmentId && datum?.contentType?.id) {
refetch();
}
};

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 { faAlignLeft } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
@@ -53,7 +54,7 @@ export const Contents = () => {
content?: IContent;
contentType?: IContentType;
}>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
// data fetching
const { onSearch, searchPayload } = useSearch<IContent>({
$eq: [{ entity: String(query.id) }],
@@ -95,7 +96,7 @@ export const Contents = () => {
},
{
label: ActionColumnLabel.Delete,
action: (content) => deleteDialogCtl.openDialog([content.id]),
action: (content) => deleteDialogCtl.openDialog(content.id),
requires: [PermissionAction.DELETE],
},
],
@@ -156,7 +157,7 @@ export const Contents = () => {
<ContentDialog {...getDisplayDialogs(editDialogCtl)} />
<ContentImportDialog
{...getDisplayDialogs(importDialogCtl)}
callback={() => {
callback={async () => {
refetch();
}}
/>

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 {
Dialog,
DialogActions,
@@ -34,7 +35,7 @@ import { slugify } from "@/utils/string";
export type ContextVarDialogProps = DialogControlProps<IContextVar>;
export const ContextVarDialog: FC<ContextVarDialogProps> = ({
open,
data,
datum: contextVar,
closeDialog,
...rest
}) => {
@@ -67,9 +68,9 @@ export const ContextVarDialog: FC<ContextVarDialogProps> = ({
control,
} = useForm<IContextVarAttributes>({
defaultValues: {
name: data?.name || "",
label: data?.label || "",
permanent: data?.permanent || false,
name: contextVar?.name || "",
label: contextVar?.label || "",
permanent: contextVar?.permanent || false,
},
});
const validationRules = {
@@ -84,8 +85,8 @@ export const ContextVarDialog: FC<ContextVarDialogProps> = ({
},
};
const onSubmitForm = async (params: IContextVarAttributes) => {
if (data) {
updateContextVar({ id: data.id, params });
if (contextVar) {
updateContextVar({ id: contextVar.id, params });
} else {
createContextVar(params);
}
@@ -96,22 +97,24 @@ export const ContextVarDialog: FC<ContextVarDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (contextVar) {
reset({
label: data.label,
name: data.name,
permanent: data.permanent,
label: contextVar.label,
name: contextVar.name,
permanent: contextVar.permanent,
});
} else {
reset();
}
}, [data, reset]);
}, [contextVar, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_context_var") : t("title.new_context_var")}
{contextVar
? t("title.edit_context_var")
: t("title.new_context_var")}
</DialogTitle>
<DialogContent>
<ContentContainer>

View File

@@ -1,16 +1,17 @@
/*
* 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 { faAsterisk } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
import { Button, Grid, Paper, Switch } from "@mui/material";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { GridColDef } from "@mui/x-data-grid";
import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog";
import { FilterTextfield } from "@/app-components/inputs/FilterTextfield";
@@ -40,7 +41,7 @@ export const ContextVars = () => {
const { toast } = useToast();
const addDialogCtl = useDialog<IContextVar>(false);
const editDialogCtl = useDialog<IContextVar>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const { onSearch, searchPayload } = useSearch<IContextVar>({
$iLike: ["label"],
@@ -69,7 +70,7 @@ export const ContextVars = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],
@@ -133,14 +134,11 @@ export const ContextVars = () => {
},
actionColumns,
];
const handleSelectionChange = (selection: GridRowSelectionModel) =>
deleteDialogCtl.setData?.(selection as string[]);
return (
<Grid container gap={3} flexDirection="column">
<ContextVarDialog {...getDisplayDialogs(addDialogCtl)} />
<ContextVarDialog {...getDisplayDialogs(editDialogCtl)} />
<DeleteDialog
{...deleteDialogCtl}
entity={EntityType.CONTEXT_VAR}
@@ -196,7 +194,10 @@ export const ContextVars = () => {
columns={columns}
{...dataGridProps}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
rowSelectionModel={deleteDialogCtl.data || []}
onRowSelectionModelChange={(rowSelectionModel) =>
deleteDialogCtl.setData?.(rowSelectionModel as string[])
}
/>
</Grid>
</Paper>

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 { Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
@@ -27,7 +28,7 @@ import { slugify } from "@/utils/string";
export type LabelDialogProps = DialogControlProps<ILabel>;
export const LabelDialog: FC<LabelDialogProps> = ({
open,
data,
datum: label,
closeDialog,
...rest
}) => {
@@ -59,9 +60,9 @@ export const LabelDialog: FC<LabelDialogProps> = ({
handleSubmit,
} = useForm<ILabelAttributes>({
defaultValues: {
name: data?.name || "",
title: data?.title || "",
description: data?.description || "",
name: label?.name || "",
title: label?.title || "",
description: label?.description || "",
},
});
const validationRules = {
@@ -72,8 +73,8 @@ export const LabelDialog: FC<LabelDialogProps> = ({
description: {},
};
const onSubmitForm = async (params: ILabelAttributes) => {
if (data) {
updateLabel({ id: data.id, params });
if (label) {
updateLabel({ id: label.id, params });
} else {
createLabel(params);
}
@@ -84,22 +85,22 @@ export const LabelDialog: FC<LabelDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (label) {
reset({
name: data.name,
title: data.title,
description: data.description,
name: label.name,
title: label.title,
description: label.description,
});
} else {
reset();
}
}, [data, reset]);
}, [label, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_label") : t("title.new_label")}
{label ? t("title.edit_label") : t("title.new_label")}
</DialogTitle>
<DialogContent>
<ContentContainer>

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 { faTags } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import { Button, Grid, Paper } from "@mui/material";
@@ -38,7 +39,7 @@ export const Labels = () => {
const { toast } = useToast();
const addDialogCtl = useDialog<ILabel>(false);
const editDialogCtl = useDialog<ILabel>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const { onSearch, searchPayload } = useSearch<ILabel>({
$or: ["name", "title"],
@@ -59,7 +60,7 @@ export const Labels = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],

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 {
Dialog,
DialogActions,
@@ -32,7 +33,7 @@ import { ILanguage, ILanguageAttributes } from "@/types/language.types";
export type LanguageDialogProps = DialogControlProps<ILanguage>;
export const LanguageDialog: FC<LanguageDialogProps> = ({
open,
data,
datum: language,
closeDialog,
...rest
}) => {
@@ -64,9 +65,9 @@ export const LanguageDialog: FC<LanguageDialogProps> = ({
control,
} = useForm<ILanguageAttributes>({
defaultValues: {
title: data?.title || "",
code: data?.code || "",
isRTL: data?.isRTL || false,
title: language?.title || "",
code: language?.code || "",
isRTL: language?.isRTL || false,
},
});
const validationRules = {
@@ -78,8 +79,8 @@ export const LanguageDialog: FC<LanguageDialogProps> = ({
},
};
const onSubmitForm = async (params: ILanguageAttributes) => {
if (data) {
updateLanguage({ id: data.id, params });
if (language) {
updateLanguage({ id: language.id, params });
} else {
createLanguage(params);
}
@@ -90,22 +91,22 @@ export const LanguageDialog: FC<LanguageDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (language) {
reset({
title: data.title,
code: data.code,
isRTL: data.isRTL,
title: language.title,
code: language.code,
isRTL: language.isRTL,
});
} else {
reset();
}
}, [data, reset]);
}, [language, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_label") : t("title.new_label")}
{language ? t("title.edit_label") : t("title.new_label")}
</DialogTitle>
<DialogContent>
<ContentContainer>

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 { Flag } from "@mui/icons-material";
import AddIcon from "@mui/icons-material/Add";
import { Button, Grid, Paper, Switch } from "@mui/material";
@@ -41,7 +42,7 @@ export const Languages = () => {
const { toast } = useToast();
const addDialogCtl = useDialog<ILanguage>(false);
const editDialogCtl = useDialog<ILanguage>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const queryClient = useQueryClient();
const hasPermission = useHasPermission();
const { onSearch, searchPayload } = useSearch<ILanguage>({
@@ -82,7 +83,7 @@ export const Languages = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
isDisabled: (row) => row.isDefault,
},

View File

@@ -1,20 +1,21 @@
/*
* 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 {
Dialog,
DialogActions,
DialogContent,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
DialogContent,
DialogActions,
} from "@mui/material";
import { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
@@ -39,8 +40,8 @@ import {
export type NlpEntityDialogProps = DialogControlProps<INlpEntity>;
export const NlpEntityDialog: FC<NlpEntityDialogProps> = ({
open,
datum: nlpEntity,
closeDialog,
data,
...rest
}) => {
const { t } = useTranslate();
@@ -70,9 +71,9 @@ export const NlpEntityDialog: FC<NlpEntityDialogProps> = ({
handleSubmit,
} = useForm<INlpEntityAttributes>({
defaultValues: {
name: data?.name || "",
doc: data?.doc || "",
lookups: data?.lookups || ["keywords"],
name: nlpEntity?.name || "",
doc: nlpEntity?.doc || "",
lookups: nlpEntity?.lookups || ["keywords"],
},
});
const validationRules = {
@@ -83,8 +84,8 @@ export const NlpEntityDialog: FC<NlpEntityDialogProps> = ({
isChecked: {},
};
const onSubmitForm = async (params: INlpEntityAttributes) => {
if (data) {
updateNlpEntity({ id: data.id, params });
if (nlpEntity) {
updateNlpEntity({ id: nlpEntity.id, params });
} else {
createNlpEntity(params);
}
@@ -95,25 +96,25 @@ export const NlpEntityDialog: FC<NlpEntityDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (nlpEntity) {
reset({
name: data.name,
doc: data.doc,
name: nlpEntity.name,
doc: nlpEntity.doc,
});
} else {
reset();
}
}, [data, reset]);
}, [nlpEntity, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_nlp_entity") : t("title.new_nlp_entity")}
{nlpEntity ? t("title.edit_nlp_entity") : t("title.new_nlp_entity")}
</DialogTitle>
<DialogContent>
<ContentContainer>
{!data ? (
{!nlpEntity ? (
<ContentItem>
<FormControl>
<FormLabel>{t("label.lookup_strategies")}</FormLabel>

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 { Dialog, DialogContent } from "@mui/material";
import { FC } from "react";
@@ -26,7 +27,7 @@ import NlpDatasetSample from "./components/NlpTrainForm";
export type NlpSampleDialogProps = DialogControlProps<INlpDatasetSample>;
export const NlpSampleDialog: FC<NlpSampleDialogProps> = ({
open,
data: sample,
datum: nlpSample,
closeDialog,
...rest
}) => {
@@ -44,10 +45,10 @@ export const NlpSampleDialog: FC<NlpSampleDialogProps> = ({
},
});
const onSubmitForm = (form: INlpSampleFormAttributes) => {
if (sample?.id) {
if (nlpSample?.id) {
updateSample(
{
id: sample.id,
id: nlpSample.id,
params: {
text: form.text,
type: form.type,
@@ -70,7 +71,7 @@ export const NlpSampleDialog: FC<NlpSampleDialogProps> = ({
{t("title.edit_nlp_sample")}
</DialogTitle>
<DialogContent>
<NlpDatasetSample sample={sample} submitForm={onSubmitForm} />
<NlpDatasetSample sample={nlpSample} submitForm={onSubmitForm} />
</DialogContent>
</Dialog>
);

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 { Dialog, DialogActions, DialogContent } from "@mui/material";
import { useRouter } from "next/router";
import { FC, useEffect } from "react";
@@ -36,44 +37,47 @@ export type NlpValueDialogProps = DialogControlProps<INlpValue> & {
export const NlpValueDialog: FC<NlpValueDialogProps> = ({
open,
datum: nlpValue,
closeDialog,
data,
canHaveSynonyms,
callback,
}) => {
const { t } = useTranslate();
const { toast } = useToast();
const { query } = useRouter();
const { refetch: refetchEntity } = useGet(data?.entity || String(query.id), {
entity: EntityType.NLP_ENTITY,
format: Format.FULL,
});
const { refetch: refetchEntity } = useGet(
nlpValue?.entity || String(query.id),
{
entity: EntityType.NLP_ENTITY,
format: Format.FULL,
},
);
const { mutateAsync: createNlpValue } = useCreate(EntityType.NLP_VALUE, {
onError: () => {
toast.error(t("message.internal_server_error"));
},
onSuccess(data) {
onSuccess(datum) {
refetchEntity();
closeDialog();
toast.success(t("message.success_save"));
callback?.(data);
callback?.(datum);
},
});
const { mutateAsync: updateNlpValue } = useUpdate(EntityType.NLP_VALUE, {
onError: () => {
toast.error(t("message.internal_server_error"));
},
onSuccess(data) {
onSuccess(datum) {
closeDialog();
toast.success(t("message.success_save"));
callback?.(data);
callback?.(datum);
},
});
const { reset, register, handleSubmit, control } =
useForm<TNlpValueAttributesWithRequiredExpressions>({
defaultValues: {
value: data?.value || "",
expressions: data?.expressions || [],
value: nlpValue?.value || "",
expressions: nlpValue?.expressions || [],
},
});
const validationRules = {
@@ -84,8 +88,8 @@ export const NlpValueDialog: FC<NlpValueDialogProps> = ({
description: {},
};
const onSubmitForm = async (params: INlpValueAttributes) => {
if (data) {
updateNlpValue({ id: data.id, params });
if (nlpValue) {
updateNlpValue({ id: nlpValue.id, params });
} else {
createNlpValue({ ...params, entity: String(query.id) });
}
@@ -96,21 +100,23 @@ export const NlpValueDialog: FC<NlpValueDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (nlpValue) {
reset({
value: data.value,
expressions: data.expressions,
value: nlpValue.value,
expressions: nlpValue.expressions,
});
} else {
reset();
}
}, [data, reset]);
}, [nlpValue, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_nlp_value") : t("title.new_nlp_entity_value")}
{nlpValue
? t("title.edit_nlp_value")
: t("title.new_nlp_entity_value")}
</DialogTitle>
<DialogContent>
<ContentContainer>

View File

@@ -1,15 +1,16 @@
/*
* 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 { Button, Chip, Grid } from "@mui/material";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { GridColDef } from "@mui/x-data-grid";
import { useRouter } from "next/router";
import { DeleteDialog } from "@/app-components/dialogs";
@@ -37,7 +38,7 @@ const NlpEntity = () => {
const router = useRouter();
const addDialogCtl = useDialog<INlpEntity>(false);
const editDialogCtl = useDialog<INlpEntity>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const { t } = useTranslate();
const { toast } = useToast();
@@ -79,7 +80,7 @@ const NlpEntity = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],
@@ -145,8 +146,6 @@ const NlpEntity = () => {
},
actionEntityColumns,
];
const handleSelectionChange = (selection: GridRowSelectionModel) =>
deleteDialogCtl.setData?.(selection as string[]);
return (
<Grid item xs={12}>
@@ -202,7 +201,10 @@ const NlpEntity = () => {
columns={nlpEntityColumns}
{...nlpEntityGrid}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
rowSelectionModel={deleteDialogCtl.data || []}
onRowSelectionModelChange={(rowSelectionModel) =>
deleteDialogCtl.setData?.(rowSelectionModel as string[])
}
/>
</Grid>
</Grid>

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 CircleIcon from "@mui/icons-material/Circle";
import ClearIcon from "@mui/icons-material/Clear";
import DeleteIcon from "@mui/icons-material/Delete";
@@ -22,7 +23,7 @@ import {
Stack,
Typography,
} from "@mui/material";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { GridColDef } from "@mui/x-data-grid";
import { useState } from "react";
import { useQueryClient } from "react-query";
@@ -74,7 +75,7 @@ export default function NlpSample() {
const { t } = useTranslate();
const { toast } = useToast();
const editDialogCtl = useDialog<INlpDatasetSample>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const queryClient = useQueryClient();
const [type, setType] = useState<NlpSampleType | "all">("all");
const [language, setLanguage] = useState<string | undefined>(undefined);
@@ -154,7 +155,7 @@ export default function NlpSample() {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],
@@ -266,8 +267,6 @@ export default function NlpSample() {
},
actionColumns,
];
const handleSelectionChange = (selection: GridRowSelectionModel) =>
deleteDialogCtl.setData?.(selection as string[]);
const handleImportChange = async (file: File) => {
await importDataset(file);
};
@@ -399,7 +398,9 @@ export default function NlpSample() {
columns={columns}
{...dataGridProps}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
onRowSelectionModelChange={(rowSelectionModel) =>
deleteDialogCtl.setData?.(rowSelectionModel as string[])
}
/>
</Grid>
</Grid>

View File

@@ -1,17 +1,18 @@
/*
* 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 { faGraduationCap } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import DeleteIcon from "@mui/icons-material/Delete";
import { Box, Button, ButtonGroup, Chip, Grid, Slide } from "@mui/material";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { GridColDef } from "@mui/x-data-grid";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
@@ -45,7 +46,7 @@ export const NlpValues = ({ entityId }: { entityId: string }) => {
const { toast } = useToast();
const addDialogCtl = useDialog<INlpValue>(false);
const editDialogCtl = useDialog<INlpValue>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const hasPermission = useHasPermission();
const [direction, setDirection] = useState<"up" | "down">("up");
const { data: nlpEntity, refetch: refetchEntity } = useGet(entityId, {
@@ -71,7 +72,7 @@ export const NlpValues = ({ entityId }: { entityId: string }) => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
},
],
t("label.operations"),
@@ -128,8 +129,6 @@ export const NlpValues = ({ entityId }: { entityId: string }) => {
}, []);
const canHaveSynonyms = nlpEntity?.lookups?.[0] === NlpLookups.keywords;
const handleSelectionChange = (selection: GridRowSelectionModel) =>
deleteDialogCtl.setData?.(selection as string[]);
return (
<Grid container gap={2} flexDirection="column">
@@ -197,7 +196,7 @@ export const NlpValues = ({ entityId }: { entityId: string }) => {
<NlpValueDialog
{...addDialogCtl}
canHaveSynonyms={canHaveSynonyms}
callback={() => {
callback={async () => {
refetchEntity();
}}
/>
@@ -215,14 +214,15 @@ export const NlpValues = ({ entityId }: { entityId: string }) => {
<NlpValueDialog
{...editDialogCtl}
canHaveSynonyms={canHaveSynonyms}
callback={() => {}}
/>
<Grid padding={1} marginTop={2} container>
<DataGrid
columns={columns}
{...dataGridProps}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
onRowSelectionModelChange={(rowSelectionModel) =>
deleteDialogCtl.setData?.(rowSelectionModel as string[])
}
/>
</Grid>
</Box>

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 DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
@@ -13,17 +14,17 @@ import {
Accordion,
AccordionDetails,
AccordionSummary,
Button,
Dialog,
DialogActions,
DialogContent,
Divider,
Grid,
MenuItem,
Paper,
Typography,
DialogContent,
DialogActions,
Button,
Divider,
} from "@mui/material";
import { useState, FC, useEffect } from "react";
import { FC, useEffect, useState } from "react";
import { IconButton } from "@/app-components/buttons/IconButton";
import { DialogTitle } from "@/app-components/dialogs/DialogTitle";
@@ -67,7 +68,7 @@ const AccordionModelHead = () => (
export const PermissionsDialog: FC<PermissionsDialogProps> = ({
open,
data,
datum: permission,
closeDialog: closeFunction,
}) => {
const { t } = useTranslate();
@@ -78,7 +79,7 @@ export const PermissionsDialog: FC<PermissionsDialogProps> = ({
hasCount: false,
},
);
const getPermisionFromCache = useGetFromCache(EntityType.PERMISSION);
const getPermissionFromCache = useGetFromCache(EntityType.PERMISSION);
const { mutateAsync: createPermission } = useCreate(EntityType.PERMISSION, {
onError: (error: Error & { statusCode?: number }) => {
if (error.statusCode === 409) {
@@ -127,7 +128,7 @@ export const PermissionsDialog: FC<PermissionsDialogProps> = ({
</DialogTitle>
<DialogContent>
<Typography fontWeight={700} sx={{ marginBottom: 2 }}>
{data?.role.name}
{permission?.role.name}
</Typography>
{models?.map((model) => {
return (
@@ -161,11 +162,8 @@ export const PermissionsDialog: FC<PermissionsDialogProps> = ({
>
<AccordionModelHead />
{model.permissions
?.map((p) => getPermisionFromCache(p))
?.filter(
(permission) =>
permission && permission.role === data?.role.id,
)
?.map((p) => getPermissionFromCache(p))
?.filter((p) => p && p.role === permission?.role.id)
.map((p) => p as IPermission)
.map(({ id, action, relation }, index) => {
return (
@@ -212,11 +210,11 @@ export const PermissionsDialog: FC<PermissionsDialogProps> = ({
color="primary"
variant="contained"
onClick={() => {
if (data?.role.id)
if (permission?.role.id)
createPermission({
...payload,
model: model.id,
role: data.role.id,
role: permission.role.id,
});
reset();
}}

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 { Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
@@ -26,7 +27,7 @@ import { IRole, IRoleAttributes } from "@/types/role.types";
export type RoleDialogProps = DialogControlProps<IRole>;
export const RoleDialog: FC<RoleDialogProps> = ({
open,
data,
datum: role,
closeDialog,
...rest
}) => {
@@ -64,8 +65,8 @@ export const RoleDialog: FC<RoleDialogProps> = ({
},
};
const onSubmitForm = async (params: IRoleAttributes) => {
if (data) {
updateRole({ id: data.id, params });
if (role) {
updateRole({ id: role.id, params });
} else {
createRole(params);
}
@@ -76,20 +77,20 @@ export const RoleDialog: FC<RoleDialogProps> = ({
}, [open, reset]);
useEffect(() => {
if (data) {
if (role) {
reset({
name: data.name,
name: role.name,
});
} else {
reset();
}
}, [data, reset]);
}, [role, reset]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<DialogTitle onClose={closeDialog}>
{data ? t("title.edit_role") : t("title.new_role")}
{role ? t("title.edit_role") : t("title.new_role")}
</DialogTitle>
<DialogContent>
<ContentContainer>

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 { faUniversalAccess } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import { Button, Grid, Paper } from "@mui/material";
@@ -39,7 +40,7 @@ export const Roles = () => {
const { toast } = useToast();
const addDialogCtl = useDialog<IRole>(false);
const editDialogCtl = useDialog<IRole>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const permissionDialogCtl = useDialog<{
role: IRole;
}>(false);
@@ -71,7 +72,7 @@ export const Roles = () => {
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],

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 {
Button,
Dialog,
@@ -14,7 +15,7 @@ import {
Grid,
} from "@mui/material";
import Link from "next/link";
import { useEffect, FC, useState } from "react";
import { FC, useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import DialogButtons from "@/app-components/buttons/DialogButtons";
@@ -39,7 +40,7 @@ export type EditSubscriberDialogProps = DialogControlProps<{
}>;
export const EditSubscriberDialog: FC<EditSubscriberDialogProps> = ({
open,
data,
datum,
closeDialog,
...rest
}) => {
@@ -65,17 +66,17 @@ export const EditSubscriberDialog: FC<EditSubscriberDialogProps> = ({
labels: {},
};
const onSubmitForm = async (params: ISubscriberAttributes) => {
if (data?.subscriber.id)
updateSubscriber({ id: data?.subscriber.id, params });
if (datum?.subscriber.id)
updateSubscriber({ id: datum?.subscriber.id, params });
};
useEffect(() => {
if (data?.subscriber) setFullName(getFullName(data?.subscriber));
if (datum?.subscriber) setFullName(getFullName(datum?.subscriber));
if (open) {
reset({ labels: data?.subscriber?.labels });
reset({ labels: datum?.subscriber?.labels });
}
}, [open, reset, data]);
}, [open, reset, datum]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...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 {
Dialog,
DialogActions,
@@ -37,7 +38,7 @@ import TranslationInput from "./TranslationInput";
export type EditTranslationDialogProps = DialogControlProps<ITranslation>;
export const EditTranslationDialog: FC<EditTranslationDialogProps> = ({
open,
data,
datum: translation,
closeDialog,
...rest
}) => {
@@ -59,15 +60,15 @@ export const EditTranslationDialog: FC<EditTranslationDialogProps> = ({
},
});
const { reset, control, handleSubmit } = useForm<ITranslationAttributes>({
defaultValues: data,
defaultValues: translation,
});
const onSubmitForm = async (params: ITranslationAttributes) => {
if (data?.id) updateTranslation({ id: data.id, params });
if (translation?.id) updateTranslation({ id: translation.id, params });
};
useEffect(() => {
if (open) reset(data);
}, [open, reset, data]);
if (open) reset(translation);
}, [open, reset, translation]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
@@ -78,7 +79,7 @@ export const EditTranslationDialog: FC<EditTranslationDialogProps> = ({
<DialogContent>
<ContentItem>
<FormLabel>{t("label.original_text")}</FormLabel>
<Typography component="p">{data?.str}</Typography>
<Typography component="p">{translation?.str}</Typography>
</ContentItem>
<ContentContainer>
{languages

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 { faLanguage } from "@fortawesome/free-solid-svg-icons";
import AutorenewIcon from "@mui/icons-material/Autorenew";
import { Button, Chip, Grid, Paper, Stack } from "@mui/material";
@@ -43,7 +44,7 @@ export const Translations = () => {
},
);
const editDialogCtl = useDialog<ITranslation>(false);
const deleteDialogCtl = useDialog<string[]>(false);
const deleteDialogCtl = useDialog<string>(false);
const { onSearch, searchPayload } = useSearch<ITranslation>({
$iLike: ["str"],
});
@@ -73,7 +74,7 @@ export const Translations = () => {
},
{
label: ActionColumnLabel.Delete,
action: (row) => deleteDialogCtl.openDialog([row.id]),
action: (row) => deleteDialogCtl.openDialog(row.id),
requires: [PermissionAction.DELETE],
},
],

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 {
Button,
Dialog,
@@ -40,7 +41,7 @@ export type EditUserDialogProps = DialogControlProps<{
export const EditUserDialog: FC<EditUserDialogProps> = ({
open,
data,
datum,
closeDialog,
...rest
}) => {
@@ -62,7 +63,7 @@ export const EditUserDialog: FC<EditUserDialogProps> = ({
reset,
formState: { errors },
} = useForm<IUserAttributes>({
defaultValues: { roles: data?.roles.map((role) => role.id) },
defaultValues: { roles: datum?.roles.map((role) => role.id) },
});
const validationRules = {
roles: {
@@ -70,18 +71,18 @@ export const EditUserDialog: FC<EditUserDialogProps> = ({
},
};
const onSubmitForm = async (params: IUserAttributes) => {
if (data?.user.id)
if (datum?.user.id)
updateUser({
id: data.user.id,
id: datum.user.id,
params,
});
};
useEffect(() => {
if (data?.user) setFullName(getFullName(data?.user));
if (datum?.user) setFullName(getFullName(datum?.user));
if (open) reset({ roles: data?.user?.roles });
}, [open, reset, data]);
if (open) reset({ roles: datum?.user?.roles });
}, [open, reset, datum]);
return (
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
@@ -108,7 +109,7 @@ export const EditUserDialog: FC<EditUserDialogProps> = ({
name="roles"
rules={validationRules.roles}
control={control}
defaultValue={data?.roles?.map(({ id }) => id) || []}
defaultValue={datum?.roles?.map(({ id }) => id) || []}
render={({ field }) => {
const { onChange, ...rest } = field;

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 ChatBubbleOutlineOutlinedIcon from "@mui/icons-material/ChatBubbleOutlineOutlined";
import SettingsApplicationsIcon from "@mui/icons-material/SettingsApplications";
import {
@@ -47,7 +48,7 @@ type TSelectedTab = "triggers" | "options" | "messages";
const BlockDialog: FC<BlockDialogProps> = ({
open,
data: block,
datum: block,
closeDialog,
...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 { Add, MoveUp } from "@mui/icons-material";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
@@ -74,8 +75,8 @@ const Diagrams = () => {
const [engine, setEngine] = useState<DiagramEngine | undefined>();
const [canvas, setCanvas] = useState<JSX.Element | undefined>();
const [selectedBlockId, setSelectedBlockId] = useState<string | undefined>();
const deleteDialogCtl = useDialog<string[]>(false);
const moveDialogCtl = useDialog<string[] | string>(false);
const deleteDialogCtl = useDialog<string>(false);
const moveDialogCtl = useDialog<string>(false);
const addCategoryDialogCtl = useDialog<ICategory>(false);
const { mutateAsync: updateBlocks } = useUpdateMany(EntityType.BLOCK);
const {
@@ -203,7 +204,8 @@ const Diagrams = () => {
setter: setSelectedBlockId,
updateFn: updateBlock,
onRemoveNode: (ids, next) => {
deleteDialogCtl.openDialog(ids);
deleteDialogCtl.setData?.(ids);
deleteDialogCtl.openDialog();
deleteCallbackRef.current = next;
},
onDbClickNode: (event, id) => {
@@ -445,7 +447,8 @@ const Diagrams = () => {
});
engine?.repaintCanvas();
};
deleteDialogCtl.openDialog(ids);
deleteDialogCtl.setData?.(ids);
deleteDialogCtl.openDialog();
}
};
const handleMoveButton = () => {
@@ -453,12 +456,11 @@ const Diagrams = () => {
const ids = selectedEntities?.map((model) => model.getID());
if (ids && selectedEntities) {
moveDialogCtl.openDialog(ids);
moveDialogCtl.setData?.(ids);
moveDialogCtl.openDialog();
}
};
const onDelete = async () => {
const ids = deleteDialogCtl?.data;
const onDelete = async (ids: string[]) => {
if (!ids || ids?.length === 0) {
return;
}
@@ -530,12 +532,15 @@ const Diagrams = () => {
<Box sx={{ width: "100%" }}>
<CategoryDialog {...getDisplayDialogs(addCategoryDialogCtl)} />
<BlockDialog {...getDisplayDialogs(editDialogCtl)} />
<DeleteDialog<string[]> {...deleteDialogCtl} callback={onDelete} />
<DeleteDialog
{...deleteDialogCtl}
callback={async (data) => {
if (Array.isArray(data)) onDelete(data);
}}
/>
<MoveDialog
open={moveDialogCtl.open}
openDialog={moveDialogCtl.openDialog}
{...moveDialogCtl}
callback={onMove}
closeDialog={moveDialogCtl.closeDialog}
categories={categories}
/>
<Grid sx={{ bgcolor: "#fff", padding: "0" }}>

View File

@@ -7,7 +7,7 @@
*/
import { DialogProps } from "@mui/material";
import { Dispatch, SetStateAction, useState } from "react";
import { useState } from "react";
export type DialogControlProps<T> = Omit<DialogControl<T>, "openDialog">;
type TCloseDialog = <E extends React.MouseEvent | Event | Object>(
@@ -15,19 +15,25 @@ type TCloseDialog = <E extends React.MouseEvent | Event | Object>(
reason?: "backdropClick" | "escapeKeyDown",
) => void;
type TFnVoid<T> = (data?: T) => void;
export type DialogControl<T = null> = DialogProps & {
data?: T;
setData?: Dispatch<SetStateAction<T | undefined>>;
callback?: TFnVoid<T>;
export type DialogControl<T = never> = DialogProps & {
data?: T[];
datum?: T;
setData?: TFnVoid<T[]>;
callback?: (data?: T | T[]) => Promise<void>;
openDialog: TFnVoid<T>;
closeDialog: TCloseDialog;
};
export const useDialog = <T,>(initialState: boolean): DialogControl<T> => {
const [open, setOpen] = useState(initialState);
const [data, setData] = useState<T | undefined>(undefined);
const openDialog: TFnVoid<T> = (data) => {
if (data) setData(data);
const [data, setData] = useState<T[] | undefined>(undefined);
const [datum, setDatum] = useState<T | undefined>(undefined);
const openDialog: TFnVoid<T> = (datum) => {
setDatum(datum);
if (datum) {
setData(undefined);
}
setOpen(true);
};
const closeDialog: TCloseDialog = (event, reason) => {
@@ -36,11 +42,18 @@ export const useDialog = <T,>(initialState: boolean): DialogControl<T> => {
}
};
return { open, openDialog, closeDialog, data, setData };
return {
open,
data,
datum,
setData,
openDialog,
closeDialog,
};
};
export const getDisplayDialogs = <T,>({
open,
closeDialog,
data,
}: DialogControl<T>): DialogControlProps<T> => ({ open, closeDialog, data });
datum,
}: DialogControl<T>): DialogControlProps<T> => ({ open, datum, closeDialog });