|
| 1 | +'use client'; |
| 2 | + |
1 | 3 | import { useQuery } from '@apollo/client'; |
2 | 4 | import { FETCH_PUBLIC_PROJECTS } from '@/graphql/request'; |
3 | 5 | import { ExpandableCard } from './expand-card'; |
| 6 | +import { useContext, useState } from 'react'; |
| 7 | +import { ProjectContext } from '../chat/code-engine/project-context'; |
| 8 | +import { redirectChatPage } from '../chat-page-navigation'; |
| 9 | +import { Button } from '@/components/ui/button'; |
| 10 | +import { RotateCwIcon } from 'lucide-react'; |
| 11 | +import { useRouter } from 'next/navigation'; |
| 12 | +import { useAuthContext } from '@/providers/AuthProvider'; |
4 | 13 |
|
5 | 14 | export function ProjectsSection() { |
6 | | - // Execute the GraphQL query with provided variables |
| 15 | + const [view, setView] = useState<'my' | 'community'>('my'); |
| 16 | + |
| 17 | + const { user } = useAuthContext(); |
| 18 | + const username = user?.username || ''; |
| 19 | + const { setChatId } = useContext(ProjectContext); |
| 20 | + const [currentChatid, setCurrentChatid] = useState(''); |
| 21 | + const router = useRouter(); |
| 22 | + |
7 | 23 | const { data, loading, error } = useQuery(FETCH_PUBLIC_PROJECTS, { |
8 | | - // Make sure strategy matches the backend definition (e.g., 'latest' or 'trending') |
9 | | - variables: { input: { size: 10, strategy: 'latest' } }, |
| 24 | + variables: { input: { size: 100, strategy: 'latest' } }, |
| 25 | + }); |
| 26 | + |
| 27 | + const allProjects = data?.fetchPublicProjects || []; |
| 28 | + |
| 29 | + // 筛选我的项目 vs 社区项目 |
| 30 | + const filteredProjects = allProjects.filter((project) => { |
| 31 | + const projectUsername = project.user?.username || ''; |
| 32 | + return view === 'my' |
| 33 | + ? projectUsername === username |
| 34 | + : projectUsername !== username; |
10 | 35 | }); |
11 | 36 |
|
12 | | - const fetchedProjects = data?.fetchPublicProjects || []; |
13 | | - |
14 | | - // Transform fetched data to match the component's expected format |
15 | | - const transformedProjects = fetchedProjects.map((project) => ({ |
16 | | - id: project.id, |
17 | | - name: project.projectName, |
18 | | - path: project.projectPath, |
19 | | - createDate: project.createdAt |
20 | | - ? new Date(project.createdAt).toISOString().split('T')[0] |
21 | | - : '2025-01-01', |
22 | | - author: project.user?.username || 'Unknown', |
23 | | - forkNum: project.subNumber || 0, |
24 | | - image: |
25 | | - project.photoUrl || `https://picsum.photos/500/250?random=${project.id}`, |
26 | | - })); |
| 37 | + const transformedProjects = filteredProjects.map((project) => { |
| 38 | + const isReady = Boolean(project.projectPath); |
| 39 | + return { |
| 40 | + id: project.id, |
| 41 | + name: project.projectName, |
| 42 | + path: project.projectPath, |
| 43 | + isReady, |
| 44 | + createDate: project.createdAt |
| 45 | + ? new Date(project.createdAt).toISOString().split('T')[0] |
| 46 | + : '2025-01-01', |
| 47 | + author: project.user?.username || 'Unknown', |
| 48 | + forkNum: project.subNumber || 0, |
| 49 | + image: |
| 50 | + project.photoUrl || |
| 51 | + `https://picsum.photos/500/250?random=${project.id}`, |
| 52 | + }; |
| 53 | + }); |
| 54 | + |
| 55 | + const handleOpenChat = (chatId: string) => { |
| 56 | + redirectChatPage(chatId, setCurrentChatid, setChatId, router); |
| 57 | + }; |
27 | 58 |
|
28 | 59 | return ( |
29 | 60 | <section className="w-full max-w-7xl mx-auto px-4"> |
30 | 61 | <div className="mb-8"> |
31 | | - {/* Header and "View All" button always visible */} |
| 62 | + {/* Header with View Toggle */} |
32 | 63 | <div className="flex items-center justify-between mb-4"> |
33 | 64 | <h2 className="text-2xl font-semibold dark:text-white"> |
34 | | - Featured Projects |
| 65 | + {view === 'my' ? 'My Projects' : 'Community Projects'} |
35 | 66 | </h2> |
36 | | - <button className="text-primary-600 dark:text-primary-400 hover:underline"> |
37 | | - View All → |
38 | | - </button> |
| 67 | + <div className="flex gap-2"> |
| 68 | + <Button |
| 69 | + variant={view === 'my' ? 'default' : 'outline'} |
| 70 | + onClick={() => setView('my')} |
| 71 | + > |
| 72 | + My Projects |
| 73 | + </Button> |
| 74 | + <Button |
| 75 | + variant={view === 'community' ? 'default' : 'outline'} |
| 76 | + onClick={() => setView('community')} |
| 77 | + > |
| 78 | + Community |
| 79 | + </Button> |
| 80 | + </div> |
39 | 81 | </div> |
40 | 82 |
|
| 83 | + {/* Content */} |
41 | 84 | {loading ? ( |
42 | 85 | <div className="text-center py-10">Loading...</div> |
43 | 86 | ) : error ? ( |
44 | | - <div className="text-center py-10">Error: {error.message}</div> |
| 87 | + <div className="text-center py-10 text-red-500"> |
| 88 | + Error: {error.message} |
| 89 | + </div> |
45 | 90 | ) : ( |
46 | | - <div> |
| 91 | + <> |
47 | 92 | {transformedProjects.length > 0 ? ( |
48 | | - <ExpandableCard projects={transformedProjects} /> |
| 93 | + <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> |
| 94 | + {transformedProjects.map((project) => |
| 95 | + view === 'my' && !project.isReady ? ( |
| 96 | + <div |
| 97 | + key={project.id} |
| 98 | + className="border border-gray-200 dark:border-zinc-700 rounded-lg p-6 flex flex-col justify-center items-center bg-gray-50 dark:bg-zinc-800 text-center" |
| 99 | + > |
| 100 | + <RotateCwIcon className="animate-spin h-6 w-6 text-gray-500 mb-3" /> |
| 101 | + <p className="text-sm text-gray-500 dark:text-gray-400 mb-4"> |
| 102 | + Generating project... |
| 103 | + </p> |
| 104 | + <Button |
| 105 | + variant="ghost" |
| 106 | + size="sm" |
| 107 | + onClick={() => handleOpenChat(project.id)} |
| 108 | + > |
| 109 | + Open Chat |
| 110 | + </Button> |
| 111 | + </div> |
| 112 | + ) : ( |
| 113 | + <ExpandableCard key={project.id} projects={[project]} /> |
| 114 | + ) |
| 115 | + )} |
| 116 | + </div> |
49 | 117 | ) : ( |
50 | | - // Show message when no projects are available |
51 | 118 | <div className="text-center py-10 text-gray-500 dark:text-gray-400"> |
52 | 119 | No projects available. |
53 | 120 | </div> |
54 | 121 | )} |
55 | | - </div> |
| 122 | + </> |
56 | 123 | )} |
57 | 124 | </div> |
58 | 125 | </section> |
|
0 commit comments