Test / components /studio /ProjectsDialog.tsx
TrikozikGames's picture
Add source files
eddc354
Raw
History Blame Contribute Delete
6.47 kB
import { useEffect, useState } from "react";
import { Download, FolderOpen, Pencil, Trash2, Upload } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import {
formatStamp,
listProjects,
removeProject,
renameProject,
type StoredProject,
} from "@/lib/studio-storage";
type Props = {
open: boolean;
onOpenChange: (v: boolean) => void;
onOpenProject: (p: StoredProject) => void;
onImportFile: () => void;
onSaveCurrent: () => void;
ownerId: string;
};
export default function ProjectsDialog({ open, onOpenChange, onOpenProject, onImportFile, onSaveCurrent, ownerId }: Props) {
const [items, setItems] = useState<StoredProject[]>([]);
const [toDelete, setToDelete] = useState<StoredProject | null>(null);
useEffect(() => {
if (open) setItems(listProjects(ownerId));
}, [open, ownerId]);
const download = (p: StoredProject) => {
const blob = new Blob([JSON.stringify(p.data, null, 2)], { type: "application/json" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = `${p.title || "proekt"}.json`;
a.click();
URL.revokeObjectURL(a.href);
};
const rename = (p: StoredProject) => {
const title = window.prompt("Новое название проекта", p.title);
if (title && title.trim()) setItems(renameProject(p.id, title.trim(), ownerId));
};
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-h-[85vh] max-w-4xl overflow-y-auto">
<DialogHeader>
<DialogTitle className="font-display text-2xl">Мои проекты</DialogTitle>
<DialogDescription>Схемы, сохранённые в памяти этого браузера.</DialogDescription>
</DialogHeader>
<div className="flex flex-wrap gap-2">
<Button size="lg" className="h-12 gap-2" onClick={onSaveCurrent} title="Сохранить текущую схему в проекты">
<FolderOpen className="h-5 w-5" /> Сохранить текущую схему
</Button>
<Button
variant="outline"
size="lg"
className="h-12 gap-2"
onClick={onImportFile}
title="Загрузить проект из файла .json"
>
<Upload className="h-5 w-5" /> Загрузить .json
</Button>
</div>
{items.length === 0 ? (
<p className="py-8 text-center text-sm text-muted-foreground">
Пока нет сохранённых проектов. Создайте схему и нажмите «Сохранить текущую схему».
</p>
) : (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{items.map((p) => (
<article key={p.id} className="overflow-hidden rounded-2xl border border-border bg-card">
<img
src={p.thumb}
alt={`Превью схемы «${p.title}»`}
className="aspect-[4/3] w-full bg-secondary object-cover"
/>
<div className="space-y-2 p-3">
<h3 className="truncate font-medium">{p.title}</h3>
<p className="text-xs text-muted-foreground">
{formatStamp(p.updatedAt)} · элементов: {p.elements}
</p>
<div className="grid grid-cols-2 gap-2">
<Button size="sm" className="h-10 gap-1" onClick={() => onOpenProject(p)} title="Открыть проект">
<FolderOpen className="h-4 w-4" /> Открыть
</Button>
<Button
variant="outline"
size="sm"
className="h-10 gap-1"
onClick={() => rename(p)}
title="Переименовать проект"
>
<Pencil className="h-4 w-4" /> Переименовать
</Button>
<Button
variant="outline"
size="sm"
className="h-10 gap-1"
onClick={() => download(p)}
title="Скачать проект файлом .json"
>
<Download className="h-4 w-4" /> Скачать
</Button>
<Button
variant="destructive"
size="sm"
className="h-10 gap-1"
onClick={() => setToDelete(p)}
title="Удалить проект"
>
<Trash2 className="h-4 w-4" /> Удалить
</Button>
</div>
</div>
</article>
))}
</div>
)}
</DialogContent>
</Dialog>
<AlertDialog open={!!toDelete} onOpenChange={(v) => !v && setToDelete(null)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Удалить проект?</AlertDialogTitle>
<AlertDialogDescription>
Вы уверены, что хотите удалить проект «{toDelete?.title}»? Действие нельзя отменить.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Отмена</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
if (toDelete) setItems(removeProject(toDelete.id, ownerId));
setToDelete(null);
}}
>
Удалить
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}