diff --git a/apps/frontend/src/modules/common/components/dataTable/dataTable.tsx b/apps/frontend/src/modules/common/components/dataTable/dataTable.tsx index df19421..ec51b6c 100644 --- a/apps/frontend/src/modules/common/components/dataTable/dataTable.tsx +++ b/apps/frontend/src/modules/common/components/dataTable/dataTable.tsx @@ -36,7 +36,7 @@ interface DataTableProps { includeColumnsSelector?: boolean; } -export function DataTable({ +export function DataTable({ columns, data, pageIndex, @@ -85,6 +85,13 @@ export function DataTable({ ...state, }, rowCount, + getRowId: (originalRow, index) => { + if ('id' in originalRow && typeof originalRow.id === 'string') { + return originalRow.id; + } + + return index.toString(); + }, }); return ( diff --git a/apps/frontend/src/modules/common/components/popupGallery/popupGallery.tsx b/apps/frontend/src/modules/common/components/popupGallery/popupGallery.tsx index 200a729..348a739 100644 --- a/apps/frontend/src/modules/common/components/popupGallery/popupGallery.tsx +++ b/apps/frontend/src/modules/common/components/popupGallery/popupGallery.tsx @@ -1,15 +1,35 @@ import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/solid'; import { Eye } from 'lucide-react'; -import { type ReactNode, useDeferredValue, useEffect, useState } from 'react'; +import { type ReactNode, useDeferredValue, useEffect, useMemo, useState } from 'react'; -import { Dialog, DialogContent, DialogTrigger } from '../../../../../@/components/ui/dialog'; +import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '../../../../../@/components/ui/dialog'; import { cn } from '@/lib/utils'; -interface ImageProps { +const VIDEO_FILE_EXTENSIONS = [ + '.mp4', + '.mkv', + '.avi', + '.mov', + '.wmv', + '.flv', + '.webm', + '.m4v', + '.mpeg', + '.3gp', + '.ogv', + '.vob', + '.rm', + '.divx', + '.mxf', + '.ts', + '.f4v', + '.mts', + '.m2ts', +]; + +interface MediaProps { source: string; - previewImageSrc: string; - alt: string; hasNext: boolean; hasPrevious: boolean; onClick: () => void; @@ -17,9 +37,11 @@ interface ImageProps { onPrevious: () => void; onClose: () => void; className?: string | undefined; + previewImageSrc: string; + alt: string; } -export function Image({ +export const Media = ({ alt, hasNext, hasPrevious, @@ -30,7 +52,7 @@ export function Image({ source, className, previewImageSrc, -}: ImageProps): JSX.Element { +}: MediaProps): JSX.Element => { const [open, setOpen] = useState(false); useEffect(() => { @@ -44,6 +66,12 @@ export function Image({ setOpen(val); }; + const isVideoFile = useMemo(() => { + return VIDEO_FILE_EXTENSIONS.some((extension) => { + return alt.endsWith(extension); + }); + }, [alt]); + return (
- {alt} + {isVideoFile && ( + + )} + {!isVideoFile && ( + {alt} + )}
+ Image container
- {alt} -
+ {isVideoFile && ( + + )} + {!isVideoFile && ( + {alt} + )} +
{hasPrevious && ( )}
@@ -88,7 +140,7 @@ export function Image({ {hasNext && ( )}
@@ -98,7 +150,7 @@ export function Image({
); -} +}; export interface PreviewableResource { url: string; @@ -120,10 +172,10 @@ export function PopupGallery({ return (
- {resources[originalPreviewResourceIndex].name} console.log('clicked')} + {}} onPrevious={() => setPreviewResourceIndex(previewResourceIndex - 1)} onNext={() => setPreviewResourceIndex(previewResourceIndex + 1)} previewImageSrc={resources[deferredIndex].url ?? ''} diff --git a/apps/frontend/src/modules/resource/components/createResourceModal/createResourceModal.tsx b/apps/frontend/src/modules/resource/components/createResourceModal/createResourceModal.tsx index 5e8ba92..c0f5a0a 100644 --- a/apps/frontend/src/modules/resource/components/createResourceModal/createResourceModal.tsx +++ b/apps/frontend/src/modules/resource/components/createResourceModal/createResourceModal.tsx @@ -31,6 +31,11 @@ export const CreateResourceModal: FC = ({ bucketName } files, setFiles, bucketName, + onUploaded: () => { + setOpen(false); + + setFileName(''); + }, }); const fileInputRef = useRef(null); @@ -77,10 +82,6 @@ export const CreateResourceModal: FC = ({ bucketName } } await upload(); - - setFileName(''); - - setOpen(false); }; const isAllowedFormat = (type: string): boolean => { diff --git a/apps/frontend/src/modules/resource/composable/useFileUpload/useFileUpload.tsx b/apps/frontend/src/modules/resource/composable/useFileUpload/useFileUpload.tsx index fc906e8..6ad1f63 100644 --- a/apps/frontend/src/modules/resource/composable/useFileUpload/useFileUpload.tsx +++ b/apps/frontend/src/modules/resource/composable/useFileUpload/useFileUpload.tsx @@ -8,6 +8,7 @@ import { useCreateResourcesMutation } from '../../api/user/mutations/createResou interface UseFileUploadPayload { files: File[]; setFiles: (files: File[]) => void; + onUploaded: () => void; bucketName: string; } @@ -19,19 +20,26 @@ interface UseFileUploadReturn { const MAX_CHUNK_SIZE = 100_000_000; // ~100MB -const FILE_UPLOAD_TIMEOUT = Number(import.meta.env['VITE_MAX_FILE_UPLOAD_TIMEOUT']); +const parsedFileUpload = Number(import.meta.env['VITE_MAX_FILE_UPLOAD_TIMEOUT']); -export const useFileUpload = ({ files, bucketName, setFiles }: UseFileUploadPayload): UseFileUploadReturn => { +const FILE_UPLOAD_TIMEOUT = Number.isNaN(parsedFileUpload) ? 18 * 1000 : parsedFileUpload; + +export const useFileUpload = ({ + files, + bucketName, + setFiles, + onUploaded, +}: UseFileUploadPayload): UseFileUploadReturn => { const queryClient = useQueryClient(); + const accessToken = useUserTokensStore((selector) => selector.accessToken); + const abortController = useRef(new AbortController()); const { toast } = useToast(); const { mutateAsync, isPending: isUploading } = useCreateResourcesMutation({}); - const accessToken = useUserTokensStore((selector) => selector.accessToken); - const upload = async (): Promise => { let runningTotalSize = 0; @@ -125,6 +133,8 @@ export const useFileUpload = ({ files, bucketName, setFiles }: UseFileUploadPayl }); } + onUploaded(); + await queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'findBucketResources' && query.queryKey[1] === bucketName, });