mirror of
https://github.com/hexastack/hexabot
synced 2025-02-22 12:28:26 +00:00
refactor(frontend): update Attachment and AttachmentThumbnail dialogs
This commit is contained in:
parent
226c08e78d
commit
773fa9f1bc
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024 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, DialogActions, DialogContent } from "@mui/material";
|
||||
import { GridEventListener } from "@mui/x-data-grid";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
|
||||
import { DialogTitle } from "@/app-components/dialogs/DialogTitle";
|
||||
import { MediaLibrary } from "@/components/media-library";
|
||||
import { DialogControlProps } from "@/hooks/useDialog";
|
||||
import { useTranslate } from "@/hooks/useTranslate";
|
||||
import { IAttachment } from "@/types/attachment.types";
|
||||
|
||||
export type AttachmentDialogProps = DialogControlProps<
|
||||
never,
|
||||
IAttachment | null
|
||||
> & { accept: string };
|
||||
|
||||
export const AttachmentDialog: FC<AttachmentDialogProps> = ({
|
||||
open,
|
||||
closeDialog,
|
||||
callback,
|
||||
accept,
|
||||
...rest
|
||||
}) => {
|
||||
const { t } = useTranslate();
|
||||
const [selected, setSelected] = useState<IAttachment | null>(null);
|
||||
const handleSelection: GridEventListener<"rowClick"> = (data) => {
|
||||
setSelected(data.row);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSelected(null);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={closeDialog} {...rest} fullWidth maxWidth="lg">
|
||||
<DialogTitle onClose={closeDialog}>
|
||||
{t("title.media_library")}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<MediaLibrary
|
||||
showTitle={false}
|
||||
onSelect={handleSelection}
|
||||
accept={accept}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!selected}
|
||||
onClick={() => {
|
||||
callback && callback(selected);
|
||||
closeDialog();
|
||||
}}
|
||||
>
|
||||
{t("button.select")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
53
frontend/src/app-components/attachment/AttachmentForm.tsx
Normal file
53
frontend/src/app-components/attachment/AttachmentForm.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 { GridEventListener } from "@mui/x-data-grid";
|
||||
import { FC, Fragment, useState } from "react";
|
||||
|
||||
import { MediaLibrary } from "@/components/media-library";
|
||||
import { IAttachment } from "@/types/attachment.types";
|
||||
import { ComponentFormProps } from "@/types/common/dialogs.types";
|
||||
|
||||
export type AttachmentFormData = {
|
||||
row?: undefined;
|
||||
accept?: string;
|
||||
onChange?: (data?: IAttachment | null) => void;
|
||||
};
|
||||
|
||||
export const AttachmentForm: FC<ComponentFormProps<AttachmentFormData>> = ({
|
||||
data,
|
||||
Wrapper = Fragment,
|
||||
WrapperProps,
|
||||
...rest
|
||||
}) => {
|
||||
const [selected, setSelected] = useState<IAttachment | null>(null);
|
||||
const handleSelection: GridEventListener<"rowClick"> = (data) => {
|
||||
setSelected(data.row);
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
open={!!WrapperProps?.open}
|
||||
onSubmit={() => {
|
||||
data?.onChange?.(selected);
|
||||
rest.onSuccess?.();
|
||||
}}
|
||||
{...WrapperProps}
|
||||
confirmButtonProps={{
|
||||
...WrapperProps?.confirmButtonProps,
|
||||
disabled: !selected,
|
||||
}}
|
||||
>
|
||||
<MediaLibrary
|
||||
showTitle={false}
|
||||
onSelect={handleSelection}
|
||||
accept={data?.accept}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 { GenericFormDialog } from "@/app-components/dialogs";
|
||||
import { ComponentFormDialogProps } from "@/types/common/dialogs.types";
|
||||
|
||||
import { AttachmentForm, AttachmentFormData } from "./AttachmentForm";
|
||||
|
||||
export const AttachmentFormDialog = <
|
||||
T extends AttachmentFormData = AttachmentFormData,
|
||||
>(
|
||||
props: ComponentFormDialogProps<T>,
|
||||
) => (
|
||||
<GenericFormDialog<T>
|
||||
Form={AttachmentForm}
|
||||
rowKey="row"
|
||||
addText="title.media_library"
|
||||
confirmButtonProps={{ value: "button.select" }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
@ -23,7 +23,7 @@ import { FC } from "react";
|
||||
|
||||
import { useDelete } from "@/hooks/crud/useDelete";
|
||||
import { useGet } from "@/hooks/crud/useGet";
|
||||
import { useDialog } from "@/hooks/useDialog";
|
||||
import { useDialogs } from "@/hooks/useDialogs";
|
||||
import useFormattedFileSize from "@/hooks/useFormattedFileSize";
|
||||
import { useHasPermission } from "@/hooks/useHasPermission";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
@ -32,7 +32,7 @@ import { EntityType } from "@/services/types";
|
||||
import { IAttachment } from "@/types/attachment.types";
|
||||
import { PermissionAction } from "@/types/permission.types";
|
||||
|
||||
import { DeleteDialog } from "../dialogs";
|
||||
import { ConfirmDialogBody } from "../dialogs";
|
||||
|
||||
const AttachmentPreview = ({
|
||||
attachment,
|
||||
@ -85,8 +85,8 @@ const AttachmentThumbnail: FC<AttachmentThumbnailProps> = ({
|
||||
});
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslate();
|
||||
const deleteDialogCtl = useDialog<string>(false);
|
||||
const { mutateAsync: deleteAttachment } = useDelete(EntityType.ATTACHMENT, {
|
||||
const dialogs = useDialogs();
|
||||
const { mutate: deleteAttachment } = useDelete(EntityType.ATTACHMENT, {
|
||||
onError: () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
@ -121,12 +121,6 @@ const AttachmentThumbnail: FC<AttachmentThumbnailProps> = ({
|
||||
hasPermission(EntityType.ATTACHMENT, PermissionAction.DELETE) &&
|
||||
onChange ? (
|
||||
<>
|
||||
<DeleteDialog
|
||||
{...deleteDialogCtl}
|
||||
callback={() => {
|
||||
deleteAttachment(attachment.id);
|
||||
}}
|
||||
/>
|
||||
<CardActions sx={{ justifyContent: "center", flex: "1 1 50%" }}>
|
||||
<Button
|
||||
color="primary"
|
||||
@ -145,8 +139,14 @@ const AttachmentThumbnail: FC<AttachmentThumbnailProps> = ({
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
startIcon={<DeleteOutlineOutlinedIcon />}
|
||||
onClick={(e) => {
|
||||
deleteDialogCtl.openDialog();
|
||||
onClick={async (e) => {
|
||||
const isConfirmed = await dialogs.confirm(
|
||||
ConfirmDialogBody,
|
||||
);
|
||||
|
||||
if (isConfirmed) {
|
||||
deleteAttachment(attachment.id);
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
|
@ -6,20 +6,19 @@
|
||||
* 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 CloudUploadIcon from "@mui/icons-material/CloudUpload";
|
||||
import FolderCopyIcon from "@mui/icons-material/FolderCopy";
|
||||
import { Box, Button, Divider, Grid, styled, Typography } from "@mui/material";
|
||||
import { ChangeEvent, DragEvent, FC, useState } from "react";
|
||||
|
||||
import { useUpload } from "@/hooks/crud/useUpload";
|
||||
import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
|
||||
import { useDialogs } from "@/hooks/useDialogs";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { useTranslate } from "@/hooks/useTranslate";
|
||||
import { EntityType } from "@/services/types";
|
||||
import { AttachmentResourceRef, IAttachment } from "@/types/attachment.types";
|
||||
|
||||
import { AttachmentDialog } from "./AttachmentDialog";
|
||||
import { AttachmentFormDialog } from "./AttachmentFormDialog";
|
||||
import AttachmentThumbnail from "./AttachmentThumbnail";
|
||||
|
||||
const FileUploadLabel = styled("label")(
|
||||
@ -82,6 +81,7 @@ const AttachmentUploader: FC<FileUploadProps> = ({
|
||||
undefined,
|
||||
);
|
||||
const { t } = useTranslate();
|
||||
const dialogs = useDialogs();
|
||||
const [isDragOver, setIsDragOver] = useState<boolean>(false);
|
||||
const { toast } = useToast();
|
||||
const { mutateAsync: uploadAttachment } = useUpload(EntityType.ATTACHMENT, {
|
||||
@ -95,7 +95,6 @@ const AttachmentUploader: FC<FileUploadProps> = ({
|
||||
onUploadComplete && onUploadComplete();
|
||||
},
|
||||
});
|
||||
const libraryDialogCtl = useDialog<never>(false);
|
||||
const stopDefaults = (e: DragEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@ -139,11 +138,6 @@ const AttachmentUploader: FC<FileUploadProps> = ({
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<AttachmentDialog
|
||||
{...getDisplayDialogs(libraryDialogCtl)}
|
||||
callback={onChange}
|
||||
accept={accept}
|
||||
/>
|
||||
<Grid container>
|
||||
<Grid item xs={enableMediaLibrary ? 5 : 12}>
|
||||
<HiddenInput
|
||||
@ -208,7 +202,16 @@ const AttachmentUploader: FC<FileUploadProps> = ({
|
||||
startIcon={<FolderCopyIcon />}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => libraryDialogCtl.openDialog()}
|
||||
onClick={() =>
|
||||
dialogs.open(
|
||||
AttachmentFormDialog,
|
||||
{
|
||||
accept,
|
||||
onChange,
|
||||
},
|
||||
{ maxWidth: "xl" },
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("button.media_library")}
|
||||
</Button>
|
||||
|
Loading…
Reference in New Issue
Block a user