Files
app/dataset-upload/page.tsx
"use client"

import * as React from "react"
import {
  FileJson,
  FileSpreadsheet,
  FileText,
  MoreVertical,
  Trash2,
  Upload,
  X,
} from "lucide-react"

import { Button } from "@/components/ui/button"

type DataFile = {
  id: string
  name: string
  size: string
  type: "csv" | "json" | "parquet"
}

const INITIAL_FILES: DataFile[] = [
  { id: "1", name: "sales-report-q1.csv", size: "2.9 MB", type: "csv" },
  { id: "2", name: "user-events-may.json", size: "3.2 MB", type: "json" },
  { id: "3", name: "churn-analysis.csv", size: "2.6 MB", type: "csv" },
]

function FileTypeIcon({ type }: { type: DataFile["type"] }) {
  if (type === "json") {
    return (
      <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-amber-100 dark:bg-amber-900/30">
        <FileJson className="h-5 w-5 text-amber-600 dark:text-amber-400" />
      </div>
    )
  }
  if (type === "parquet") {
    return (
      <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-violet-100 dark:bg-violet-900/30">
        <FileText className="h-5 w-5 text-violet-600 dark:text-violet-400" />
      </div>
    )
  }
  return (
    <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-emerald-100 dark:bg-emerald-900/30">
      <FileSpreadsheet className="h-5 w-5 text-emerald-600 dark:text-emerald-400" />
    </div>
  )
}

export default function DatasetUpload() {
  const [files, setFiles] = React.useState<DataFile[]>(INITIAL_FILES)
  const [dragging, setDragging] = React.useState(false)
  const [openMenu, setOpenMenu] = React.useState<string | null>(null)

  function removeFile(id: string) {
    setFiles((f) => f.filter((file) => file.id !== id))
    setOpenMenu(null)
  }

  React.useEffect(() => {
    function handleClick() {
      setOpenMenu(null)
    }
    document.addEventListener("click", handleClick)
    return () => document.removeEventListener("click", handleClick)
  }, [])

  return (
    <div className="flex min-h-screen items-center justify-center p-4">
      <div className="bg-background w-full max-w-md rounded-2xl shadow-xl">
        {/* Header */}
        <div className="flex items-start justify-between p-6 pb-4">
          <div>
            <h2 className="text-foreground text-xl font-medium tracking-tight">
              Import dataset files
            </h2>
            <p className="text-muted-foreground mt-1 text-sm font-light">
              Add CSV, JSON, or Parquet files to power your analytics.
            </p>
          </div>
          <button
            className="text-muted-foreground hover:bg-muted hover:text-foreground mt-0.5 ml-4 rounded-md p-1 transition-colors"
            aria-label="Close"
          >
            <X className="h-4 w-4" />
          </button>
        </div>

        <div className="space-y-4 px-6 pb-6">
          {/* Drop zone */}
          <div
            onDragOver={(e) => {
              e.preventDefault()
              setDragging(true)
            }}
            onDragLeave={() => setDragging(false)}
            onDrop={(e) => {
              e.preventDefault()
              setDragging(false)
            }}
            className={`flex flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors ${
              dragging
                ? "border-primary bg-primary/5"
                : "border-border bg-muted/30 hover:border-muted-foreground/40 hover:bg-muted/50"
            }`}
          >
            <div className="bg-muted flex h-12 w-12 items-center justify-center rounded-xl">
              <Upload className="text-muted-foreground h-6 w-6" />
            </div>
            <p className="text-foreground text-sm font-light">
              Drag and drop a file or{" "}
              <button className="hover:text-primary font-normal underline underline-offset-2">
                Browse
              </button>
            </p>
            <p className="text-muted-foreground text-xs font-light">
              CSV, JSON, XLSX or Parquet up to 50 MB
            </p>
          </div>

          {/* File list */}
          {files.length > 0 && (
            <ul className="space-y-2">
              {files.map((file) => (
                <li
                  key={file.id}
                  className="border-border bg-background flex items-center gap-3 rounded-xl border px-3 py-3"
                >
                  <FileTypeIcon type={file.type} />
                  <div className="min-w-0 flex-1">
                    <p className="text-foreground truncate text-sm font-normal">
                      {file.name}
                    </p>
                    <p className="text-muted-foreground text-xs font-light">
                      {file.size}
                    </p>
                  </div>
                  <div className="relative">
                    <button
                      onClick={(e) => {
                        e.stopPropagation()
                        setOpenMenu(openMenu === file.id ? null : file.id)
                      }}
                      className="text-muted-foreground hover:bg-muted hover:text-foreground rounded-md p-1.5 transition-colors"
                      aria-label="File options"
                    >
                      <MoreVertical className="h-4 w-4" />
                    </button>
                    {openMenu === file.id && (
                      <div className="border-border bg-background absolute right-0 z-10 mt-1 w-36 rounded-lg border py-1 shadow-lg">
                        <button
                          onClick={() => removeFile(file.id)}
                          className="text-destructive hover:bg-muted flex w-full items-center gap-2 px-3 py-1.5 text-sm"
                        >
                          <Trash2 className="h-3.5 w-3.5" />
                          Remove file
                        </button>
                      </div>
                    )}
                  </div>
                </li>
              ))}
            </ul>
          )}

          {/* Continue button */}
          <Button className="w-full" size="lg">
            Continue
          </Button>
        </div>
      </div>
    </div>
  )
}
Dataset file uploader with drag-and-drop zone and file list
dataset-upload-01