deepsite / components /my-projects /load-project.tsx
enzostvs's picture
enzostvs HF Staff
Load existing project
0b4acc5
raw
history blame
5.03 kB
import { useState } from "react";
import { Import } from "lucide-react";
import { Project } from "@/types";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import Loading from "@/components/loading";
import { Input } from "../ui/input";
import { toast } from "sonner";
import { api } from "@/lib/api";
export const LoadProject = ({
addProject,
}: {
addProject: (project: Project) => void;
}) => {
const [open, setOpen] = useState(false);
const [url, setUrl] = useState<string>("");
const [isLoading, setIsLoading] = useState(false);
const checkIfUrlIsValid = (url: string) => {
// should match a hugging face spaces URL like: https://huggingface.co/spaces/username/project or https://hf.co/spaces/username/project
const urlPattern = new RegExp(
/^(https?:\/\/)?(huggingface\.co|hf\.co)\/spaces\/([\w-]+)\/([\w-]+)$/,
"i"
);
return urlPattern.test(url);
};
const handleClick = async () => {
if (isLoading) return; // Prevent multiple clicks while loading
if (!url) {
toast.error("Please enter a URL.");
return;
}
if (!checkIfUrlIsValid(url)) {
toast.error("Please enter a valid Hugging Face Spaces URL.");
return;
}
const [username, namespace] = url
.replace("https://huggingface.co/spaces/", "")
.replace("https://hf.co/spaces/", "")
.split("/");
setIsLoading(true);
try {
const response = await api.post(`/me/projects/${username}/${namespace}`);
console.log("response", response);
toast.success("Project imported successfully!");
setOpen(false);
setUrl("");
addProject(response.data.project);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
toast.error(
error?.response?.data?.error ?? "Failed to import the project."
);
} finally {
setIsLoading(false);
}
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline">
<Import className="size-4 mr-1.5" />
Load existing Project
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md !p-0 !rounded-3xl !bg-white !border-neutral-100 overflow-hidden text-center">
<DialogTitle className="hidden" />
<header className="bg-neutral-50 p-6 border-b border-neutral-200/60">
<div className="flex items-center justify-center -space-x-4 mb-3">
<div className="size-11 rounded-full bg-pink-200 shadow-2xs flex items-center justify-center text-2xl opacity-50">
🎨
</div>
<div className="size-13 rounded-full bg-amber-200 shadow-2xl flex items-center justify-center text-3xl z-2">
🥳
</div>
<div className="size-11 rounded-full bg-sky-200 shadow-2xs flex items-center justify-center text-2xl opacity-50">
💎
</div>
</div>
<p className="text-2xl font-semibold text-neutral-950">
Import a Project
</p>
<p className="text-base text-neutral-500 mt-1.5">
Enter the URL of your Hugging Face Space to import an existing
project.
</p>
</header>
<main className="space-y-4 px-9 pb-9 pt-2">
<div>
<p className="text-sm text-neutral-700 mb-2">
Enter your Hugging Face Space
</p>
<Input
type="text"
placeholder="https://huggingface.com/spaces/username/project"
value={url}
onChange={(e) => setUrl(e.target.value)}
onBlur={(e) => {
const inputUrl = e.target.value.trim();
if (!inputUrl) {
setUrl("");
return;
}
if (!checkIfUrlIsValid(inputUrl)) {
toast.error("Please enter a valid URL.");
return;
}
setUrl(inputUrl);
}}
className="!bg-white !border-neutral-300 !text-neutral-800 !placeholder:text-neutral-400 selection:!bg-blue-100"
/>
</div>
<div>
<p className="text-sm text-neutral-700 mb-2">
Then, let&apos;s import it!
</p>
<Button
variant="black"
onClick={handleClick}
className="relative w-full"
>
{isLoading ? (
<>
<Loading
overlay={false}
className="ml-2 size-4 animate-spin"
/>
Fetching your Space...
</>
) : (
<>Import your Space</>
)}
</Button>
</div>
</main>
</DialogContent>
</Dialog>
);
};