All files / app/src/shared/ui/file file.tsx

87.87% Statements 58/66
85.71% Branches 6/7
40% Functions 2/5
87.87% Lines 58/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 1011x                                         1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x   5x     5x               5x 5x 5x 5x 5x 5x   5x 5x 5x   5x         5x 1x 1x 1x 1x   3x 3x 3x 3x 3x 3x 3x 3x   3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x   3x   5x 5x 5x   5x  
import { ReactNode } from 'react'
import { FCWithClassName, FileModel } from '@/shared/@types'
import { forceDownload, getFileExtension, getFileNameWithoutExtension } from '@/shared/helpers'
import { Button } from '../button'
import { httpClient, useTranslate } from '@/shared/lib'
import cn from 'classnames'
import Loading from '@/shared/assets/icons/common/loading.svg'
import OpenEye from '@/shared/assets/icons/common/open-eye.svg'
 
export interface FileProps {
  file: FileModel
  onRemove?: (file: FileModel) => void
  extraContent?: ReactNode
  isSaved?: boolean
  showExtension?: boolean
  onView?: (file: FileModel) => void
  isDownload?: boolean
  removeButtonClassName?: string
  fileNameClassName?: string
}
 
export const File: FCWithClassName<FileProps> = ({
  file,
  className,
  onRemove,
  extraContent = <></>,
  isSaved,
  showExtension = true,
  isDownload = true,
  onView,
  removeButtonClassName,
  fileNameClassName,
}) => {
  const { t } = useTranslate(['common'])
  const isLoading = file.loading
 
  const fileExtension = getFileExtension(file)
 
  // Current blob size limit is around 500MB for browsers
  const downloadResource = async (src: string, filename: string) => {
    if (typeof window !== 'undefined' && isDownload) {
      const response = await httpClient<Blob>({ url: src, responseType: 'blob' }).then(res => res.data)
 
      forceDownload(window.URL.createObjectURL(response), filename).then(url => window.URL.revokeObjectURL(url))
    }
  }
 
  return (
    <div className='flex flex-col gap-2 cursor-pointer'>
      <div
        data-testid='file'
        className={cn(
          `flex items-center gap-5 max-w-full w-fit rounded-base transition-colors
          bg-border hover:bg-gray-quaternary active:bg-gray py-3 px-5`,
          className
        )}
        onClick={() => downloadResource(file.fullPath, file.originalName)}
      >
        {onView && (
          <Button variant='icon' onClick={() => onView(file)}>
            <OpenEye className='stroke-blue-gray' />
          </Button>
        )}
        {isLoading ? (
          <>
            <Loading data-testid='file-preloader' className='mr-5 w-7 h-7 fill-text-secondary animate-spin shrink-0' />
            <h4 className='text-text-secondary'>{t('File loading')}...</h4>
          </>
        ) : (
          <>
            <h4 className={cn('text-black truncate max-w-[185px]', fileNameClassName)}>
              {getFileNameWithoutExtension(file.originalName)}
            </h4>
            {showExtension && (
              <h4 className='text-black'>
                <span className='text-text-secondary h4'> .{fileExtension}</span>
              </h4>
            )}
            {!isSaved && onRemove && (
              <Button
                data-testId='file-delete-button'
                onClick={e => {
                  e.preventDefault()
                  e.stopPropagation()
                  onRemove(file)
                }}
                className={removeButtonClassName}
                color='secondary'
                variant='text'
              >
                <h3>{t('Delete')}</h3>
              </Button>
            )}
          </>
        )}
      </div>
      {extraContent}
    </div>
  )
}