2026-05-18 11:55:55 +05:30
|
|
|
|
import { useState, useRef } from "react";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import { User, X, Loader2 } from "lucide-react";
|
2026-04-16 15:33:22 +05:30
|
|
|
|
import axios from "axios";
|
2026-04-14 17:33:21 +05:30
|
|
|
|
|
|
|
|
|
|
interface BytescaleUploaderProps {
|
|
|
|
|
|
value: string;
|
|
|
|
|
|
onChange: (url: string) => void;
|
2026-05-18 11:55:55 +05:30
|
|
|
|
folderPath:
|
2026-05-26 11:56:22 +05:30
|
|
|
|
| "/health-packages"
|
|
|
|
|
|
| "/seo"
|
2026-05-18 11:55:55 +05:30
|
|
|
|
| "/doctors"
|
|
|
|
|
|
| "/departments"
|
|
|
|
|
|
| "/news"
|
|
|
|
|
|
| "/blog"
|
2026-05-21 14:36:17 +05:30
|
|
|
|
| "/doctor-og";
|
2026-04-14 17:33:21 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 15:33:22 +05:30
|
|
|
|
export function BytescaleUploader({
|
|
|
|
|
|
value,
|
|
|
|
|
|
onChange,
|
|
|
|
|
|
folderPath,
|
|
|
|
|
|
}: BytescaleUploaderProps) {
|
2026-04-16 19:49:06 +05:30
|
|
|
|
const baseURL = import.meta.env.VITE_API_URL;
|
2026-04-14 17:33:21 +05:30
|
|
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const onFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
|
const file = event.target.files?.[0];
|
|
|
|
|
|
if (!file) return;
|
|
|
|
|
|
|
2026-04-16 15:33:22 +05:30
|
|
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
|
|
|
|
alert("File is too large (Max 5MB)");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 17:33:21 +05:30
|
|
|
|
setIsUploading(true);
|
2026-04-16 15:33:22 +05:30
|
|
|
|
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append("file", file);
|
|
|
|
|
|
formData.append("folderPath", folderPath);
|
|
|
|
|
|
|
2026-04-14 17:33:21 +05:30
|
|
|
|
try {
|
2026-04-16 19:49:06 +05:30
|
|
|
|
const response = await axios.post(`${baseURL}/upload`, formData, {
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
"Content-Type": "multipart/form-data",
|
2026-04-14 17:33:21 +05:30
|
|
|
|
},
|
2026-04-16 19:49:06 +05:30
|
|
|
|
});
|
2026-04-14 17:33:21 +05:30
|
|
|
|
|
2026-05-18 11:55:55 +05:30
|
|
|
|
const { fileUrl } = response.data;
|
2026-04-14 17:33:21 +05:30
|
|
|
|
onChange(fileUrl);
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
console.error("Upload Error:", e);
|
2026-04-16 15:33:22 +05:30
|
|
|
|
const errorMessage =
|
|
|
|
|
|
e.response?.data?.error || e.message || "Upload failed";
|
|
|
|
|
|
alert(`Upload Error: ${errorMessage}`);
|
2026-04-14 17:33:21 +05:30
|
|
|
|
} 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 ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={value}
|
|
|
|
|
|
className="w-16 h-16 rounded-full object-cover border-2 border-primary/20"
|
2026-04-16 15:33:22 +05:30
|
|
|
|
alt="Preview"
|
2026-04-14 17:33:21 +05:30
|
|
|
|
/>
|
|
|
|
|
|
<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"
|
|
|
|
|
|
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 Photo"
|
|
|
|
|
|
) : (
|
|
|
|
|
|
"Upload Photo"
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{value && (
|
|
|
|
|
|
<p className="text-xs text-amber-600 pl-[72px]">
|
2026-04-16 15:33:22 +05:30
|
|
|
|
⚠️ Make sure to save the changes by clicking the "Save Changes"
|
|
|
|
|
|
button.
|
2026-04-14 17:33:21 +05:30
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|