Team Ai
Apppublic

IDKHowToCodeFr/tinyml-backend

sourceHugging Faceupdated 2d agoView on Hugging Face
1likes
page.tsx139 linesDownload Raw Back to mlops
1"use client";2 3import { motion } from "framer-motion";4import Link from "next/link";5import { useState } from "react";6 7const easeFluid: [number, number, number, number] = [0.16, 1, 0.3, 1];8 9const staggerContainer = {10  hidden: { opacity: 0 },11  show: {12    opacity: 1,13    transition: { staggerChildren: 0.1, delayChildren: 0.1 }14  }15};16 17const fadeUp = {18  hidden: { opacity: 0, y: 20 },19  show: { opacity: 1, y: 0, transition: { duration: 0.8, ease: easeFluid } }20};21 22export default function MLOps() {23  const [file, setFile] = useState<File | null>(null);24  const [status, setStatus] = useState<"idle" | "uploading" | "success" | "error">("idle");25  const [message, setMessage] = useState("");26 27  const handleUpload = async () => {28    if (!file) return;29    setStatus("uploading");30 31    const formData = new FormData();32    formData.append("file", file);33 34    try {35      const res = await fetch((process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000") + "/retrain", {36        method: "POST",37        body: formData,38      });39      const data = await res.json();40 41      if (data.error) {42        setStatus("error");43        setMessage(data.error);44      } else {45        setStatus("success");46        setMessage(data.message);47      }48    } catch (err: any) {49      setStatus("error");50      setMessage(err.message || "Upload failed");51    }52  };53 54  return (55    <main className="min-h-dvh bg-canvas-cream flex flex-col font-mono selection:bg-hazard selection:text-white">56      {/* Top Nav (Mechanical) */}57 58      <motion.div59        variants={staggerContainer}60        initial="hidden"61        animate="show"62        className="max-w-250 mx-auto w-full px-8 py-16 flex-1 flex flex-col"63      >64        <motion.header variants={fadeUp} className="mb-12 border-b-4 border-ink pb-8 flex justify-between items-end">65          <div>66            <div className="text-[12px] text-hazard font-bold tracking-widest mb-4">67              /// PIPELINE_CONTROL68            </div>69            <h1 className="text-[clamp(3rem,6vw,6rem)]">70              MODEL<br />OPERATIONS71            </h1>72          </div>73        </motion.header>74 75        <motion.div variants={fadeUp} className="border-2 border-ink bg-white p-8">76          <div className="border-b-2 border-ink pb-4 mb-8">77            <h2 className="text-[16px] font-bold tracking-wider uppercase text-ink">{'< BATCH_RETRAIN >'}</h2>78            <p className="text-[13px] text-slate uppercase mt-2">Append telemetry data and rebuild the edge ensemble.</p>79          </div>80 81          <div className="flex flex-col gap-6">82            <label83              htmlFor="dropzone-file"84              className="flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-ink bg-canvas-cream hover:bg-hazard hover:text-white transition-colors cursor-pointer group relative overflow-hidden"85            >86              <div className="absolute inset-0 pointer-events-none opacity-20 bg-[linear-gradient(45deg,var(--ink-black)_25%,transparent_25%,transparent_50%,var(--ink-black)_50%,var(--ink-black)_75%,transparent_75%,transparent)] bg-size-[20px_20px]" />87              <div className="flex flex-col items-center justify-center pt-5 pb-6 relative z-10">88                <motion.div89                  initial={{ scale: 0.9, opacity: 0 }}90                  animate={{ scale: 1, opacity: 1 }}91                  key={file ? 'selected' : 'unselected'}92                  className="text-[24px] font-bold mb-4 group-hover:text-white"93                >94                  {file ? '[ FILE_SELECTED ]' : '[ SELECT_CSV ]'}95                </motion.div>96                <p className="mb-2 text-[14px] font-bold uppercase">97                  {file ? file.name : "CLICK TO UPLOAD DATASET"}98                </p>99                <p className="text-[12px] uppercase opacity-70">REQUIRES MATCHING SCHEMA</p>100              </div>101              <input102                id="dropzone-file"103                type="file"104                className="sr-only"105                accept=".csv"106                onChange={(e) => setFile(e.target.files?.[0] || null)}107                aria-label="Upload CSV Dataset"108              />109            </label>110 111            <div className="flex justify-between items-center border-t-2 border-ink pt-6 mt-2" aria-live="polite">112              <motion.div113                key={status}114                initial={{ opacity: 0, x: -10 }}115                animate={{ opacity: 1, x: 0 }}116                className="text-[13px] font-bold uppercase tracking-wider"117              >118                {status === "uploading" && <span className="text-slate">PROCESSING...</span>}119                {status === "error" && <span className="text-hazard">ERR: {message}</span>}120                {status === "success" && <span className="text-ink bg-canvas-cream px-2 py-1 border border-ink">SYS: {message}</span>}121              </motion.div>122 123              <motion.button124                whileHover={{ scale: 1.02, backgroundColor: "var(--color-hazard)", borderColor: "var(--color-hazard)" }}125                whileTap={{ scale: 0.98 }}126                onClick={handleUpload}127                disabled={!file || status === "uploading"}128                className="inline-flex items-center justify-center h-12 px-8 bg-ink text-white text-[14px] font-bold uppercase tracking-widest border-2 border-ink disabled:opacity-50 disabled:pointer-events-none"129              >130                EXECUTE &gt;&gt;131              </motion.button>132            </div>133          </div>134        </motion.div>135      </motion.div>136    </main>137  );138}139