feat(frontend): apply frontend updates (context)

This commit is contained in:
Mohamed Marrouchi 2025-01-08 18:11:28 +01:00
parent 994c8857e9
commit 7be61150ac
11 changed files with 121 additions and 41 deletions

View File

@ -1,18 +1,19 @@
/*
* 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 { Box, FormHelperText, FormLabel } from "@mui/material";
import { forwardRef } from "react";
import { useGet } from "@/hooks/crud/useGet";
import { useHasPermission } from "@/hooks/useHasPermission";
import { EntityType } from "@/services/types";
import { IAttachment } from "@/types/attachment.types";
import { IAttachment, TAttachmentContext } from "@/types/attachment.types";
import { PermissionAction } from "@/types/permission.types";
import AttachmentThumbnail from "./AttachmentThumbnail";
@ -28,6 +29,7 @@ type AttachmentThumbnailProps = {
onChange?: (id: string | null, mimeType: string | null) => void;
error?: boolean;
helperText?: string;
context: TAttachmentContext;
};
const AttachmentInput = forwardRef<HTMLDivElement, AttachmentThumbnailProps>(
@ -42,6 +44,7 @@ const AttachmentInput = forwardRef<HTMLDivElement, AttachmentThumbnailProps>(
onChange,
error,
helperText,
context,
},
ref,
) => {
@ -81,6 +84,7 @@ const AttachmentInput = forwardRef<HTMLDivElement, AttachmentThumbnailProps>(
accept={accept}
enableMediaLibrary={enableMediaLibrary}
onChange={handleChange}
context={context}
/>
) : null}
{helperText ? (

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 CloudUploadIcon from "@mui/icons-material/CloudUpload";
import FolderCopyIcon from "@mui/icons-material/FolderCopy";
import { Box, Button, Divider, Grid, styled, Typography } from "@mui/material";
@ -16,7 +17,7 @@ import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
import { useToast } from "@/hooks/useToast";
import { useTranslate } from "@/hooks/useTranslate";
import { EntityType } from "@/services/types";
import { IAttachment } from "@/types/attachment.types";
import { IAttachment, TAttachmentContext } from "@/types/attachment.types";
import { AttachmentDialog } from "./AttachmentDialog";
import AttachmentThumbnail from "./AttachmentThumbnail";
@ -67,6 +68,7 @@ export type FileUploadProps = {
enableMediaLibrary?: boolean;
onChange?: (data?: IAttachment | null) => void;
onUploadComplete?: () => void;
context: TAttachmentContext;
};
const AttachmentUploader: FC<FileUploadProps> = ({
@ -74,6 +76,7 @@ const AttachmentUploader: FC<FileUploadProps> = ({
enableMediaLibrary,
onChange,
onUploadComplete,
context,
}) => {
const [attachment, setAttachment] = useState<IAttachment | undefined>(
undefined,
@ -97,34 +100,40 @@ const AttachmentUploader: FC<FileUploadProps> = ({
e.stopPropagation();
e.preventDefault();
};
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) {
const file = event.target.files.item(0);
const handleUpload = (file: File | null) => {
if (file) {
const acceptedTypes = accept.split(",");
const isValidType = acceptedTypes.some(
(type) =>
file.type === type || file.name.endsWith(type.replace(".*", "")),
const isValidType = acceptedTypes.some((mimeType) => {
const [type, subtype] = mimeType.split("/");
if (!type || !subtype) return false; // Ensure valid MIME type
return (
file.type === mimeType ||
(subtype === "*" && file.type.startsWith(`${type}/`))
);
});
if (!isValidType) {
toast.error(t("message.invalid_file_type"));
return;
}
uploadAttachment(file);
uploadAttachment({ file, context });
}
};
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) {
const file = event.target.files.item(0);
handleUpload(file);
}
};
const onDrop = (event: DragEvent<HTMLElement>) => {
if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {
const file = event.dataTransfer.files.item(0);
if (file) {
uploadAttachment(file);
}
handleUpload(file);
}
};

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 { 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 { IAttachment, TAttachmentContext } from "@/types/attachment.types";
import { PermissionAction } from "@/types/permission.types";
import AttachmentThumbnail from "./AttachmentThumbnail";
@ -27,6 +28,7 @@ type MultipleAttachmentInputProps = {
onChange?: (ids: string[]) => void;
error?: boolean;
helperText?: string;
context: TAttachmentContext;
};
const MultipleAttachmentInput = forwardRef<
@ -44,6 +46,7 @@ const MultipleAttachmentInput = forwardRef<
onChange,
error,
helperText,
context,
},
ref,
) => {
@ -106,6 +109,7 @@ const MultipleAttachmentInput = forwardRef<
accept={accept}
enableMediaLibrary={enableMediaLibrary}
onChange={(attachment) => handleChange(attachment)}
context={context}
/>
)}
{helperText && (

View File

@ -116,6 +116,7 @@ const ContentFieldInput: React.FC<ContentFieldInput> = ({
value={field.value?.payload?.id}
accept={MIME_TYPES["images"].join(",")}
format="full"
context="content_attachment"
/>
);
default:

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 CloseIcon from "@mui/icons-material/Close";
import { Button, Dialog, DialogActions, DialogContent } from "@mui/material";
import { FC, useState } from "react";
@ -80,6 +81,7 @@ export const ContentImportDialog: FC<ContentImportDialogProps> = ({
}}
label=""
value={attachmentId}
context="content_attachment"
/>
</ContentItem>
</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 KeyIcon from "@mui/icons-material/Key";
import { FormControlLabel, MenuItem, Switch } from "@mui/material";
import { ControllerRenderProps } from "react-hook-form";
@ -185,6 +186,7 @@ const SettingInput: React.FC<RenderSettingInputProps> = ({
accept={MIME_TYPES["images"].join(",")}
format="full"
size={128}
context="setting_attachment"
/>
);
@ -197,6 +199,7 @@ const SettingInput: React.FC<RenderSettingInputProps> = ({
accept={MIME_TYPES["images"].join(",")}
format="full"
size={128}
context="setting_attachment"
/>
);
default:

View File

@ -69,6 +69,7 @@ const AttachmentMessageForm = () => {
},
});
}}
context="block_attachment"
/>
);
}}

View File

@ -9,6 +9,7 @@
import { useMutation, useQueryClient } from "react-query";
import { QueryType, TMutationOptions } from "@/services/types";
import { TAttachmentContext } from "@/types/attachment.types";
import { IBaseSchema, IDynamicProps, TType } from "@/types/base.types";
import { useEntityApiClient } from "../useApiClient";
@ -23,7 +24,12 @@ export const useUpload = <
>(
entity: TEntity,
options?: Omit<
TMutationOptions<TBasic, Error, File, TBasic>,
TMutationOptions<
TBasic,
Error,
{ file: File; context: TAttachmentContext },
TBasic
>,
"mutationFn" | "mutationKey"
>,
) => {
@ -33,8 +39,8 @@ export const useUpload = <
const { invalidate = true, ...otherOptions } = options || {};
return useMutation({
mutationFn: async (variables: File) => {
const data = await api.upload(variables);
mutationFn: async ({ file, context }) => {
const data = await api.upload(file, context);
const { entities, result } = normalizeAndCache(data);
// Invalidate all counts & collections

View File

@ -9,6 +9,7 @@
import { AxiosInstance, AxiosResponse } from "axios";
import { TAttachmentContext } from "@/types/attachment.types";
import { ILoginAttributes } from "@/types/auth/login.types";
import { IUserPermissions } from "@/types/auth/permission.types";
import { StatsType } from "@/types/bot-stat.types";
@ -301,7 +302,7 @@ export class EntityApiClient<TAttr, TBasic, TFull> extends ApiClient {
return data;
}
async upload(file: File) {
async upload(file: File, context?: TAttachmentContext) {
const { _csrf } = await this.getCsrf();
const formData = new FormData();
@ -311,11 +312,17 @@ export class EntityApiClient<TAttr, TBasic, TFull> extends ApiClient {
TBasic[],
AxiosResponse<TBasic[]>,
FormData
>(`${ROUTES[this.type]}/upload?_csrf=${_csrf}`, formData, {
>(
`${ROUTES[this.type]}/upload?_csrf=${_csrf}${
context ? `&context=${context}` : ""
}`,
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
});
},
);
return data[0];
}

View File

@ -1,14 +1,43 @@
/*
* 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 { Format } from "@/services/types";
import { IBaseSchema, IFormat } from "./base.types";
import { EntityType, Format } from "@/services/types";
import { IBaseSchema, IFormat, OmitPopulate } from "./base.types";
import { ISubscriber } from "./subscriber.types";
import { IUser } from "./user.types";
/**
* Defines the types of owners for an attachment,
* indicating whether the file belongs to a User or a Subscriber.
*/
export enum AttachmentOwnerType {
User = "User",
Subscriber = "Subscriber",
}
export type TAttachmentOwnerType = `${AttachmentOwnerType}`;
/**
* Defines the various contexts in which an attachment can exist.
* These contexts influence how the attachment is uploaded, stored, and accessed:
*/
export enum AttachmentContext {
SettingAttachment = "setting_attachment", // Attachments related to app settings, restricted to users with specific permissions.
UserAvatar = "user_avatar", // Avatar files for users, only the current user can upload, accessible to those with appropriate permissions.
SubscriberAvatar = "subscriber_avatar", // Avatar files for subscribers, uploaded programmatically, accessible to authorized users.
BlockAttachment = "block_attachment", // Files sent by the bot, public or private based on the channel and user authentication.
ContentAttachment = "content_attachment", // Files in the knowledge base, usually public but could vary based on specific needs.
MessageAttachment = "message_attachment", // Files sent or received via messages, uploaded programmatically, accessible to users with inbox permissions.;
}
export type TAttachmentContext = `${AttachmentContext}`;
export interface IAttachmentAttributes {
name: string;
@ -17,8 +46,21 @@ export interface IAttachmentAttributes {
location: string;
url: string;
channel?: Record<string, any>;
context: TAttachmentContext;
ownerType: TAttachmentOwnerType;
owner: string | null;
}
export interface IAttachmentStub extends IBaseSchema, IAttachmentAttributes {}
export interface IAttachmentStub
extends IBaseSchema,
OmitPopulate<IAttachmentAttributes, EntityType.ATTACHMENT> {}
export interface IAttachment extends IAttachmentStub, IFormat<Format.BASIC> {}
export interface IAttachment extends IAttachmentStub, IFormat<Format.BASIC> {
owner: string | null;
}
export interface ISubscriberAttachmentFull
extends IAttachmentStub,
IFormat<Format.FULL> {
owner: (ISubscriber | IUser)[];
}

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 { GridPaginationModel, GridSortModel } from "@mui/x-data-grid";
import { EntityType, Format } from "@/services/types";
@ -109,7 +110,7 @@ export const POPULATE_BY_TYPE = {
[EntityType.MENUTREE]: [],
[EntityType.LANGUAGE]: [],
[EntityType.TRANSLATION]: [],
[EntityType.ATTACHMENT]: [],
[EntityType.ATTACHMENT]: ["owner"],
[EntityType.CUSTOM_BLOCK]: [],
[EntityType.CUSTOM_BLOCK_SETTINGS]: [],
[EntityType.CHANNEL]: [],