This commit is contained in:
Daniil
2026-04-04 14:51:40 +03:00
parent 10a1d28f77
commit 0523ef3d72
191 changed files with 12065 additions and 2658 deletions
@@ -0,0 +1,141 @@
"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, markStepCompleted, goNext } = 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,
)
setFileKey(result.file_path, result.file_url, result.filename ?? null)
markStepCompleted("upload")
goNext()
} catch {
setError(ERROR_UPLOAD_FAILED)
} finally {
setIsUploading(false)
}
},
[projectId, setFileKey, markStepCompleted, goNext],
)
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>
)
}