Files
main_frontend/src/features/project/UploadStep/UploadStep.tsx
T
2026-04-27 23:28:28 +03:00

144 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import type { IUploadStepProps } from "./UploadStep.d"
import type { JSX } from "react"
import { Upload } from "lucide-react"
import { FunctionComponent, useCallback, useRef, useState } from "react"
import cs from "classnames"
import { uploadFileWithProgress } from "@shared/api/uploadFile"
import { useWizard } from "@shared/context/WizardContext"
import { Button } from "@shared/ui"
import styles from "./UploadStep.module.scss"
const ACCEPTED_VIDEO_TYPES = "video/*"
const ERROR_UPLOAD_FAILED = "Не удалось загрузить файл"
export const UploadStep: FunctionComponent<IUploadStepProps> = ({
className,
}): JSX.Element => {
const { projectId, setFileKey } = useWizard()
const [isDragging, setIsDragging] = useState(false)
const [isUploading, setIsUploading] = useState(false)
const [progress, setProgress] = useState(0)
const [error, setError] = useState<string | null>(null)
const inputRef = useRef<HTMLInputElement>(null)
const handleUpload = useCallback(
async (file: File) => {
setIsUploading(true)
setProgress(0)
setError(null)
try {
const result = await uploadFileWithProgress(
file,
`projects/${projectId}`,
setProgress,
)
await setFileKey(
result.file_path,
result.file_id,
result.filename ?? null,
)
} catch {
setError(ERROR_UPLOAD_FAILED)
} finally {
setIsUploading(false)
}
},
[projectId, setFileKey],
)
const handleFileChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) handleUpload(file)
/* Reset input so re-selecting the same file triggers change */
e.target.value = ""
},
[handleUpload],
)
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
const file = e.dataTransfer.files[0]
if (file) handleUpload(file)
},
[handleUpload],
)
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault()
setIsDragging(true)
}, [])
const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
}, [])
return (
<div className={cs(styles.root, className)} data-testid="UploadStep">
<div className={styles.content}>
<div
className={cs(styles.dropZone, {
[styles.dropZoneActive]: isDragging,
[styles.dropZoneUploading]: isUploading,
})}
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onClick={() => inputRef.current?.click()}
>
<input
ref={inputRef}
type="file"
accept={ACCEPTED_VIDEO_TYPES}
className={styles.fileInput}
onChange={handleFileChange}
disabled={isUploading}
/>
<Upload size={48} className={styles.icon} />
{isUploading ? (
<>
<p className={styles.title}>Загрузка файла...</p>
<div className={styles.progressTrack}>
<div
className={styles.progressBar}
style={{ width: `${progress}%` }}
/>
</div>
<p className={styles.progressLabel}>{Math.round(progress)}%</p>
</>
) : (
<>
<p className={styles.title}>Перетащите видеофайл сюда</p>
<p className={styles.subtitle}>или нажмите для выбора файла</p>
<Button
variant="outline"
size="sm"
type="button"
onClick={(e) => {
e.stopPropagation()
inputRef.current?.click()
}}
>
Выбрать файл
</Button>
</>
)}
{error && <p className={styles.error}>{error}</p>}
</div>
</div>
</div>
)
}