twenty/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx
Paul Rastoin 4a4e65fe4a
[REFACTOR] Twenty UI multi barrel (#11301)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.

## TODO
- [x] Manual migrate twenty-website twenty-ui imports

## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them

### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app

Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
2025-04-03 09:47:55 +00:00

136 lines
4.2 KiB
TypeScript

import styled from '@emotion/styled';
import { ChangeEvent, useRef, useState } from 'react';
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
import { AttachmentList } from '@/activities/files/components/AttachmentList';
import { DropZone } from '@/activities/files/components/DropZone';
import { useAttachments } from '@/activities/files/hooks/useAttachments';
import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttachmentFile';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { isDefined } from 'twenty-shared/utils';
import {
AnimatedPlaceholder,
AnimatedPlaceholderEmptyContainer,
AnimatedPlaceholderEmptySubTitle,
AnimatedPlaceholderEmptyTextContainer,
AnimatedPlaceholderEmptyTitle,
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
} from 'twenty-ui/layout';
import { Button } from 'twenty-ui/input';
import { IconPlus } from 'twenty-ui/display';
const StyledAttachmentsContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
overflow: auto;
`;
const StyledFileInput = styled.input`
display: none;
`;
const StyledDropZoneContainer = styled.div`
height: 100%;
`;
export const Attachments = ({
targetableObject,
}: {
targetableObject: ActivityTargetableObject;
}) => {
const inputFileRef = useRef<HTMLInputElement>(null);
const { attachments, loading } = useAttachments(targetableObject);
const { uploadAttachmentFile } = useUploadAttachmentFile();
const [isDraggingFile, setIsDraggingFile] = useState(false);
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (isDefined(e.target.files)) onUploadFile?.(e.target.files[0]);
};
const handleUploadFileClick = () => {
inputFileRef?.current?.click?.();
};
const onUploadFile = async (file: File) => {
await uploadAttachmentFile(file, targetableObject);
};
const isAttachmentsEmpty = !attachments || attachments.length === 0;
if (loading && isAttachmentsEmpty) {
return <SkeletonLoader />;
}
if (isAttachmentsEmpty) {
return (
<StyledDropZoneContainer onDragEnter={() => setIsDraggingFile(true)}>
{isDraggingFile ? (
<DropZone
setIsDraggingFile={setIsDraggingFile}
onUploadFile={onUploadFile}
/>
) : (
<AnimatedPlaceholderEmptyContainer
// eslint-disable-next-line react/jsx-props-no-spreading
{...EMPTY_PLACEHOLDER_TRANSITION_PROPS}
>
<AnimatedPlaceholder type="noFile" />
<AnimatedPlaceholderEmptyTextContainer>
<AnimatedPlaceholderEmptyTitle>
No Files
</AnimatedPlaceholderEmptyTitle>
<AnimatedPlaceholderEmptySubTitle>
There are no associated files with this record.
</AnimatedPlaceholderEmptySubTitle>
</AnimatedPlaceholderEmptyTextContainer>
<StyledFileInput
ref={inputFileRef}
onChange={handleFileChange}
type="file"
/>
{!hasObjectReadOnlyPermission && (
<Button
Icon={IconPlus}
title="Add file"
variant="secondary"
onClick={handleUploadFileClick}
/>
)}
</AnimatedPlaceholderEmptyContainer>
)}
</StyledDropZoneContainer>
);
}
return (
<StyledAttachmentsContainer>
<StyledFileInput
ref={inputFileRef}
onChange={handleFileChange}
type="file"
/>
<AttachmentList
targetableObject={targetableObject}
title="All"
attachments={attachments ?? []}
button={
!hasObjectReadOnlyPermission && (
<Button
Icon={IconPlus}
size="small"
variant="secondary"
title="Add file"
onClick={handleUploadFileClick}
></Button>
)
}
/>
</StyledAttachmentsContainer>
);
};