142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
import { useState, useRef } from 'react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { User, X, Loader2, Video } from 'lucide-react';
|
||
import axios from 'axios';
|
||
|
||
interface BytescaleUploaderProps {
|
||
value: string;
|
||
onChange: (url: string) => void;
|
||
folderPath:
|
||
| '/health-packages'
|
||
| '/seo'
|
||
| '/doctors'
|
||
| '/departments'
|
||
| '/news'
|
||
| '/blog'
|
||
| '/doctor-og'
|
||
| '/homepage-banners'
|
||
| '/insurance-partners'
|
||
| '/accreditations';
|
||
}
|
||
|
||
export function BytescaleUploader({ value, onChange, folderPath }: BytescaleUploaderProps) {
|
||
const baseURL = import.meta.env.VITE_API_URL;
|
||
const [isUploading, setIsUploading] = useState(false);
|
||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||
|
||
const isVideo = (url: string) => {
|
||
return /\.(mp4|webm|ogg)$/i.test(url);
|
||
};
|
||
|
||
const onFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||
const file = event.target.files?.[0];
|
||
|
||
if (!file) return;
|
||
|
||
const maxSize = file.type.startsWith('video/') ? 10 * 1024 * 1024 : 5 * 1024 * 1024;
|
||
|
||
if (file.size > maxSize) {
|
||
alert(file.type.startsWith('video/') ? 'Video is too large (Max 10MB)' : 'Image is too large (Max 5MB)');
|
||
return;
|
||
}
|
||
|
||
setIsUploading(true);
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
formData.append('folderPath', folderPath);
|
||
|
||
try {
|
||
const response = await axios.post(`${baseURL}/upload`, formData, {
|
||
headers: {
|
||
'Content-Type': 'multipart/form-data',
|
||
},
|
||
});
|
||
|
||
const { fileUrl } = response.data;
|
||
onChange(fileUrl);
|
||
} catch (e: any) {
|
||
console.error('Upload Error:', e);
|
||
const errorMessage = e.response?.data?.error || e.message || 'Upload failed';
|
||
alert(`Upload Error: ${errorMessage}`);
|
||
} finally {
|
||
setIsUploading(false);
|
||
|
||
if (fileInputRef.current) {
|
||
fileInputRef.current.value = '';
|
||
}
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="flex flex-col gap-2 p-3 border rounded-md bg-muted/5">
|
||
<div className="flex items-center gap-4">
|
||
<div className="relative">
|
||
{value ? (
|
||
<>
|
||
{isVideo(value) ? (
|
||
<video src={value} className="w-20 h-20 rounded-md object-cover border-2 border-primary/20" controls />
|
||
) : (
|
||
<img
|
||
src={value}
|
||
className="w-16 h-16 rounded-full object-cover border-2 border-primary/20"
|
||
alt="Preview"
|
||
/>
|
||
)}
|
||
|
||
<button
|
||
type="button"
|
||
onClick={() => onChange('')}
|
||
className="absolute -top-1 -right-1 bg-destructive text-white rounded-full p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||
>
|
||
<X className="w-3 h-3" />
|
||
</button>
|
||
</>
|
||
) : (
|
||
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center">
|
||
{isUploading ? (
|
||
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||
) : (
|
||
<User className="w-8 h-8 text-muted-foreground" />
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<input
|
||
type="file"
|
||
ref={fileInputRef}
|
||
onChange={onFileSelected}
|
||
accept="image/jpeg,image/png,image/webp,video/mp4,video/webm,video/ogg"
|
||
className="hidden"
|
||
/>
|
||
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
size="sm"
|
||
disabled={isUploading}
|
||
onClick={() => fileInputRef.current?.click()}
|
||
>
|
||
{isUploading ? (
|
||
<>
|
||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||
Uploading...
|
||
</>
|
||
) : value ? (
|
||
'Change File'
|
||
) : (
|
||
'Upload Image / Video'
|
||
)}
|
||
</Button>
|
||
</div>
|
||
|
||
{value && (
|
||
<p className="text-xs text-amber-600 pl-[72px]">
|
||
⚠️ Make sure to save the changes by clicking the "Save Changes" button.
|
||
</p>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|