feat: add multiple attachment input

This commit is contained in:
hexastack 2024-10-31 19:56:23 +01:00
parent 1792b125d3
commit f7ae8682e0
3 changed files with 140 additions and 2 deletions

View File

@ -66,12 +66,14 @@ export type FileUploadProps = {
accept: string;
enableMediaLibrary?: boolean;
onChange?: (data?: IAttachment | null) => void;
onUploadComplete?: () => void;
};
const AttachmentUploader: FC<FileUploadProps> = ({
accept,
enableMediaLibrary,
onChange,
onUploadComplete,
}) => {
const [attachment, setAttachment] = useState<IAttachment | undefined>(
undefined,
@ -87,6 +89,7 @@ const AttachmentUploader: FC<FileUploadProps> = ({
toast.success(t("message.success_save"));
setAttachment(data);
onChange && onChange(data);
onUploadComplete && onUploadComplete();
},
});
const libraryDialogCtl = useDialog<never>(false);

View File

@ -0,0 +1,122 @@
/*
* 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 { Box, Button, FormHelperText, FormLabel } from "@mui/material";
import { forwardRef, useState } from "react";
import { useHasPermission } from "@/hooks/useHasPermission";
import { EntityType } from "@/services/types";
import { IAttachment } from "@/types/attachment.types";
import { PermissionAction } from "@/types/permission.types";
import AttachmentThumbnail from "./AttachmentThumbnail";
import AttachmentUploader from "./AttachmentUploader";
type MultipleAttachmentInputProps = {
label: string;
value: string[];
format: "small" | "basic" | "full";
accept: string;
enableMediaLibrary?: boolean;
size?: number;
onChange?: (ids: string[]) => void;
error?: boolean;
helperText?: string;
};
const MultipleAttachmentInput = forwardRef<
HTMLDivElement,
MultipleAttachmentInputProps
>(
(
{
label,
value,
format,
accept,
enableMediaLibrary = true,
size,
onChange,
error,
helperText,
},
ref,
) => {
const [attachments, setAttachments] = useState<string[]>(value);
const [uploadKey, setUploadKey] = useState(Date.now());
const hasPermission = useHasPermission();
const handleChange = (attachment?: IAttachment | null, index?: number) => {
if (attachment) {
const updatedAttachments = [...attachments];
if (index !== undefined) {
updatedAttachments[index] = attachment.id;
} else {
updatedAttachments.push(attachment.id);
}
setAttachments(updatedAttachments);
onChange && onChange(updatedAttachments);
setUploadKey(Date.now());
}
};
const handleRemove = (index: number) => {
const updatedAttachments = attachments.filter((_, i) => i !== index);
setAttachments(updatedAttachments);
onChange && onChange(updatedAttachments);
setUploadKey(Date.now());
};
return (
<Box ref={ref}>
<FormLabel
component="label"
style={{ display: "inline-block", marginBottom: 8 }}
>
{label}
</FormLabel>
{attachments.map((attachmentId, index) => (
<Box
key={attachmentId}
sx={{ display: "flex", alignItems: "center", mb: 2 }}
>
<AttachmentThumbnail
id={attachmentId}
format={format}
size={size}
onChange={(newAttachment) => handleChange(newAttachment, index)}
/>
<Button
onClick={() => handleRemove(index)}
sx={{ ml: 2 }}
variant="outlined"
color="secondary"
>
Remove
</Button>
</Box>
))}
{hasPermission(EntityType.ATTACHMENT, PermissionAction.CREATE) && (
<AttachmentUploader
key={uploadKey}
accept={accept}
enableMediaLibrary={enableMediaLibrary}
onChange={(attachment) => handleChange(attachment)}
/>
)}
{helperText && (
<FormHelperText error={error}>{helperText}</FormHelperText>
)}
</Box>
);
},
);
MultipleAttachmentInput.displayName = "MultipleAttachmentInput";
export default MultipleAttachmentInput;

View File

@ -11,6 +11,7 @@ import { FormControlLabel, MenuItem, Switch } from "@mui/material";
import { ControllerRenderProps } from "react-hook-form";
import AttachmentInput from "@/app-components/attachment/AttachmentInput";
import MultipleAttachmentInput from "@/app-components/attachment/MultipleAttachmentInput";
import { Adornment } from "@/app-components/inputs/Adornment";
import AutoCompleteEntitySelect from "@/app-components/inputs/AutoCompleteEntitySelect";
import { Input } from "@/app-components/inputs/Input";
@ -20,7 +21,7 @@ import { useTranslate } from "@/hooks/useTranslate";
import { EntityType, Format } from "@/services/types";
import { IBlock } from "@/types/block.types";
import { IHelper } from "@/types/helper.types";
import { ISetting } from "@/types/setting.types";
import { ISetting, SettingType } from "@/types/setting.types";
import { MIME_TYPES } from "@/utils/attachment";
interface RenderSettingInputProps {
@ -45,7 +46,7 @@ const SettingInput: React.FC<RenderSettingInputProps> = ({
});
switch (setting.type) {
case "text":
case SettingType.text:
case "textarea":
return (
<Input
@ -185,6 +186,18 @@ const SettingInput: React.FC<RenderSettingInputProps> = ({
size={128}
/>
);
case SettingType.multiple_attachment:
return (
<MultipleAttachmentInput
label={label}
{...field}
value={field.value}
accept={MIME_TYPES["images"].join(",")}
format="full"
size={128}
/>
);
default:
return <Input {...field} />;
}