feat: Add welcome card (#1073)

Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
Co-authored-by: Johannes <johannes@formbricks.com>
This commit is contained in:
Naitik Kapadia
2023-10-17 14:57:57 +05:30
committed by GitHub
parent 06b9d4f5f9
commit 2c1abf8782
36 changed files with 881 additions and 266 deletions
+154
View File
@@ -0,0 +1,154 @@
"use client";
import { PhotoIcon, TrashIcon } from "@heroicons/react/24/outline";
import { ArrowUpTrayIcon } from "@heroicons/react/24/solid";
import { FileIcon } from "lucide-react";
import { useState } from "react";
import toast from "react-hot-toast";
import { uploadFile } from "./lib/fileUpload";
interface FileInputProps {
allowedFileExtensions: string[];
environmentId: string | undefined;
onFileUpload: (uploadedUrl: string | undefined) => void;
fileUrl: string | undefined;
}
const FileInput: React.FC<FileInputProps> = ({
allowedFileExtensions,
environmentId,
onFileUpload,
fileUrl,
}) => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [isUploaded, setIsUploaded] = useState<boolean>(!!fileUrl);
const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
e.preventDefault();
e.stopPropagation();
e.dataTransfer.dropEffect = "copy";
};
const handleDrop = async (e: React.DragEvent<HTMLLabelElement>) => {
e.preventDefault();
e.stopPropagation();
const file = e.dataTransfer.files[0];
if (
file &&
file.type &&
allowedFileExtensions.includes(file.type.substring(file.type.lastIndexOf("/") + 1))
) {
setIsUploaded(false);
setSelectedFile(file);
const response = await uploadFile(file, allowedFileExtensions, environmentId);
setIsUploaded(true);
onFileUpload(response.data.url);
} else {
toast.error("File not supported");
}
};
return (
<label
htmlFor="selectedFile"
className="relative flex h-52 w-full cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-slate-300 bg-slate-50 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-700 dark:hover:border-slate-500 dark:hover:bg-slate-600 dark:hover:bg-slate-800"
onDragOver={(e) => handleDragOver(e)}
onDrop={(e) => handleDrop(e)}>
{isUploaded && fileUrl ? (
<>
<div className="absolute inset-0 mr-4 mt-2 flex items-start justify-end gap-4">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-300 hover:bg-slate-200/50 text-slate-800 hover:text-slate-900 bg-opacity-50">
<label htmlFor="modifyFile">
<PhotoIcon className="h-5 text-slate-700 hover:text-slate-900 cursor-pointer" />
<input
type="file"
id="modifyFile"
name="modifyFile"
accept={allowedFileExtensions.map((ext) => `.${ext}`).join(",")}
className="hidden"
onChange={async (e) => {
const selectedFile = e.target?.files?.[0];
if (selectedFile) {
setIsUploaded(false);
setSelectedFile(selectedFile);
const response = await uploadFile(selectedFile, allowedFileExtensions, environmentId);
setIsUploaded(true);
onFileUpload(response.data.url);
}
}}
/>
</label>
</div>
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-300 hover:bg-slate-200/50 bg-opacity-50">
<TrashIcon className="h-5 text-slate-700 hover:text-slate-900" onClick={() => onFileUpload(undefined)} />
</div>
</div>
{fileUrl.endsWith("jpg") || fileUrl.endsWith("jpeg") || fileUrl.endsWith("png") ? (
<img
src={fileUrl}
alt="Company Logo"
className="max-h-full max-w-full rounded-lg object-contain"
/>
) : (
<div className="flex flex-col items-center justify-center pb-6 pt-5">
<FileIcon className="h-6 text-slate-500" />
<p className="dark.text-slate-400 mt-2 text-sm text-slate-500">
<span className="font-semibold">{fileUrl.split("/").pop()}</span>
</p>
</div>
)}
</>
) : !isUploaded && selectedFile ? (
<>
{selectedFile.type.startsWith("image/") ? (
<img
src={URL.createObjectURL(selectedFile)}
alt="Company Logo"
className="max-h-full max-w-full rounded-lg object-contain"
/>
) : (
<div className="flex flex-col items-center justify-center pb-6 pt-5">
<FileIcon className="h-6 text-slate-500" />
<p className="dark.text-slate-400 mt-2 text-sm text-slate-500">
<span className="font-semibold">{selectedFile.name}</span>
</p>
</div>
)}
<div className="hover.bg-opacity-60 absolute inset-0 flex items-center justify-center bg-black bg-opacity-20 transition-opacity duration-300">
<label htmlFor="selectedFile" className="cursor-pointer text-sm font-semibold text-white">
Uploading
</label>
</div>
</>
) : (
<div className="flex flex-col items-center justify-center pb-6 pt-5">
<ArrowUpTrayIcon className="h-6 text-slate-500" />
<p className="dark.text-slate-400 mt-2 text-sm text-slate-500">
<span className="font-semibold">Click or drag to upload files.</span>
</p>
<input
type="file"
id="selectedFile"
name="selectedFile"
accept={allowedFileExtensions.map((ext) => `.${ext}`).join(",")}
className="hidden"
onChange={async (e) => {
const selectedFile = e.target?.files?.[0];
if (selectedFile) {
setIsUploaded(false);
setSelectedFile(selectedFile);
const response = await uploadFile(selectedFile, allowedFileExtensions, environmentId);
setIsUploaded(true);
onFileUpload(response.data.url);
}
}}
/>
</div>
)}
</label>
);
};
export default FileInput;
+40
View File
@@ -0,0 +1,40 @@
const uploadFile = async (
file: File | Blob,
allowedFileExtensions: string[],
environmentId: string | undefined
) => {
try {
if (!(file instanceof Blob) || !(file instanceof File)) {
throw new Error(`Invalid file type. Expected Blob or File, but received ${typeof file}`);
}
const fileBuffer = await file.arrayBuffer();
const payload = {
fileBuffer: Array.from(new Uint8Array(fileBuffer)),
fileName: file.name,
contentType: file.type,
allowedFileExtensions: allowedFileExtensions,
environmentId: environmentId,
};
const response = await fetch("/api/v1/management/storage", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Upload failed with status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error("Upload error:", error);
throw error;
}
};
export { uploadFile };