Files
time_to_leave/apps/web/src/app/calendar/FileTab.tsx
T

92 lines
2.7 KiB
TypeScript

"use client";
import React, { useRef, useState } from "react";
import Button from "@/app/ui/Button";
/**
* File upload tab for importing `.ics` calendar files.
*
* Supports both file picker selection and drag-and-drop.
* Sends the file content to `/api/calendar/parse` for server-side ICS parsing.
*/
type FileTabProps = {
onLoadCalendar: (file: File) => Promise<void>;
loading: boolean;
error: string | null;
className?: string;
};
const FileTab: React.FC<FileTabProps> = ({ onLoadCalendar, loading, error, className = "" }) => {
const fileInputRef = useRef<HTMLInputElement>(null);
const [dragging, setDragging] = useState(false);
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
handleFileUpload(file);
}
};
const handleFileUpload = async (file: File) => {
try {
await onLoadCalendar(file);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
} catch (err) {
console.error("Error uploading file:", err);
}
};
const handleDragEnter = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setDragging(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setDragging(false);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setDragging(true);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setDragging(false);
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFileUpload(files[0]);
}
};
return (
<div className={`p-1 ${className}`}>
<div
className={`mb-4 cursor-pointer rounded-2xl border border-dashed p-8 text-center text-sm transition-colors ${dragging ? "border-[#D946EF] bg-[#B23CFF]/12 text-white" : "border-white/20 bg-white/[0.04] text-[#F4F1EA]/68 hover:border-[#D946EF]/50 hover:bg-white/[0.06]"}`}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDragOver={handleDragOver}
onDrop={handleDrop}
onClick={() => fileInputRef.current?.click()}
>
<input type="file" ref={fileInputRef} onChange={handleFileSelect} accept=".ics" className="hidden" />
{loading ? <>Loading calendar...</> : <>Click to upload or drag and drop an .ics file here</>}
</div>
{error && <div className="mb-4 text-sm text-[#FF2D8D]">{error}</div>}
<Button onClick={() => fileInputRef.current?.click()} disabled={loading}>
{loading ? <>Select File</> : <>Select File</>}
</Button>
</div>
);
};
export default FileTab;