application [wireframe-renderer-web] view page [pages/index] development
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
@@ -24,6 +25,7 @@ interface DrawingElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CanvasPage() {
|
export default function CanvasPage() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const [tool, setTool] = useState<Tool>("pen");
|
const [tool, setTool] = useState<Tool>("pen");
|
||||||
const [isDrawing, setIsDrawing] = useState(false);
|
const [isDrawing, setIsDrawing] = useState(false);
|
||||||
@@ -198,6 +200,8 @@ export default function CanvasPage() {
|
|||||||
const user = await auth.currentUser();
|
const user = await auth.currentUser();
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
|
const projectIdFromUrl = searchParams.get("projectId");
|
||||||
|
|
||||||
// Load projects
|
// Load projects
|
||||||
const { data: userProjects } = await entities
|
const { data: userProjects } = await entities
|
||||||
.from("nvwa_project")
|
.from("nvwa_project")
|
||||||
@@ -206,8 +210,14 @@ export default function CanvasPage() {
|
|||||||
|
|
||||||
if (userProjects && userProjects.length > 0) {
|
if (userProjects && userProjects.length > 0) {
|
||||||
setProjects(userProjects);
|
setProjects(userProjects);
|
||||||
// Use first project or create new one
|
// Use specified project or first one
|
||||||
const project = userProjects[0];
|
let project;
|
||||||
|
if (projectIdFromUrl) {
|
||||||
|
project = userProjects.find(p => p.id === parseInt(projectIdFromUrl));
|
||||||
|
}
|
||||||
|
if (!project) {
|
||||||
|
project = userProjects[0];
|
||||||
|
}
|
||||||
setCurrentProject(project);
|
setCurrentProject(project);
|
||||||
|
|
||||||
// Load latest wireframe for this project
|
// Load latest wireframe for this project
|
||||||
@@ -230,7 +240,7 @@ export default function CanvasPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create default project
|
// Create default project if no projects exist
|
||||||
const { data: newProject } = await entities
|
const { data: newProject } = await entities
|
||||||
.from("nvwa_project")
|
.from("nvwa_project")
|
||||||
.insert({
|
.insert({
|
||||||
|
|||||||
@@ -1,11 +1,150 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Plus, Folder } from "lucide-react";
|
||||||
|
import { entities, auth } from "@/lib/nvwa";
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
|
const [projects, setProjects] = useState([]);
|
||||||
|
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
|
||||||
|
const [newProjectName, setNewProjectName] = useState("");
|
||||||
|
const [newProjectDescription, setNewProjectDescription] = useState("");
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadProjects();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadProjects = async () => {
|
||||||
|
try {
|
||||||
|
const user = await auth.currentUser();
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
const { data } = await entities
|
||||||
|
.from("nvwa_project")
|
||||||
|
.select("*")
|
||||||
|
.eq("user_id", user.id)
|
||||||
|
.order("updated_at", { ascending: false });
|
||||||
|
|
||||||
|
setProjects(data || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load projects:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createProject = async () => {
|
||||||
|
try {
|
||||||
|
const user = await auth.currentUser();
|
||||||
|
if (!user || !newProjectName.trim()) return;
|
||||||
|
|
||||||
|
const { data } = await entities
|
||||||
|
.from("nvwa_project")
|
||||||
|
.insert({
|
||||||
|
name: newProjectName,
|
||||||
|
description: newProjectDescription,
|
||||||
|
user_id: user.id
|
||||||
|
})
|
||||||
|
.select();
|
||||||
|
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
setProjects(prev => [data[0], ...prev]);
|
||||||
|
setNewProjectName("");
|
||||||
|
setNewProjectDescription("");
|
||||||
|
setIsCreateDialogOpen(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create project:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openProject = (projectId) => {
|
||||||
|
navigate(`/canvas?projectId=${projectId}`);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
<div className="container mx-auto p-4">
|
||||||
<div className="text-center">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<h1 className="text-2xl font-bold mb-4">欢迎</h1>
|
<h1 className="text-3xl font-bold">项目列表</h1>
|
||||||
<p className="text-muted-foreground">这是首页</p>
|
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button>
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
创建新项目
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>创建新项目</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="name">项目名称</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
value={newProjectName}
|
||||||
|
onChange={(e) => setNewProjectName(e.target.value)}
|
||||||
|
placeholder="输入项目名称"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="desc">描述</Label>
|
||||||
|
<Textarea
|
||||||
|
id="desc"
|
||||||
|
value={newProjectDescription}
|
||||||
|
onChange={(e) => setNewProjectDescription(e.target.value)}
|
||||||
|
placeholder="输入项目描述(可选)"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button onClick={createProject} disabled={!newProjectName.trim()}>
|
||||||
|
创建
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{projects.map((project) => (
|
||||||
|
<Card
|
||||||
|
key={project.id}
|
||||||
|
className="cursor-pointer hover:shadow-md transition-shadow"
|
||||||
|
onClick={() => openProject(project.id)}
|
||||||
|
>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center">
|
||||||
|
<Folder className="w-5 h-5 mr-2" />
|
||||||
|
{project.name}
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-sm text-muted-foreground mb-2">
|
||||||
|
{project.description || "无描述"}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
创建于 {new Date(project.created_at).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{projects.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<Folder className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
|
||||||
|
<h3 className="text-lg font-medium mb-2">还没有项目</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">创建一个新项目开始你的设计之旅</p>
|
||||||
|
<Button onClick={() => setIsCreateDialogOpen(true)}>
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
创建第一个项目
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user