From e3c7a43483aef2ef7fb37fb55690f83d5fb438da Mon Sep 17 00:00:00 2001 From: Che <30403707+Che-Zhu@users.noreply.github.com> Date: Wed, 25 Feb 2026 14:54:01 +0800 Subject: [PATCH] refactor: Migrate project list page to (list) route group (v2.0.0-alpha-4) - Move app/home/ to app/(dashboard)/projects/(list)/ using route group to isolate Sidebar layout from project detail pages - Add router.refresh() polling (3s) for real-time project status updates - Fix projectName uppercase issue in delete confirmation dialog - Remove old components/features/projectList/ directory - Remove old app/(dashboard)/projects/page.tsx - Bump version to 2.0.0-alpha.4 - Add baseline-browser-mapping devDependency Co-Authored-By: Claude Sonnet 4.5 --- .../_components/create-project-card.tsx | 0 .../(list)}/_components/home-page-content.tsx | 14 +- .../_components/page-header-with-filter.tsx | 0 .../_components/project-actions-menu.tsx | 2 +- .../(list)}/_components/project-card.tsx | 0 .../(list)}/_components/project-list.tsx | 0 .../(list)}/_components/status-config.ts | 0 .../projects/(list)}/layout.tsx | 0 .../projects/(list)}/page.tsx | 0 app/(dashboard)/projects/page.tsx | 20 --- .../features/projectList/EmptyProjectCard.tsx | 33 ---- .../features/projectList/ProjectCard.tsx | 89 ---------- .../projectList/ProjectCardDropdown.tsx | 155 ------------------ .../projectList/ProjectCardSkeleton.tsx | 36 ---- .../projectList/ProjectListContent.tsx | 36 ---- .../projectList/ProjectListHeader.tsx | 60 ------- package.json | 3 +- pnpm-lock.yaml | 12 +- 18 files changed, 24 insertions(+), 436 deletions(-) rename app/{home => (dashboard)/projects/(list)}/_components/create-project-card.tsx (100%) rename app/{home => (dashboard)/projects/(list)}/_components/home-page-content.tsx (67%) rename app/{home => (dashboard)/projects/(list)}/_components/page-header-with-filter.tsx (100%) rename app/{home => (dashboard)/projects/(list)}/_components/project-actions-menu.tsx (98%) rename app/{home => (dashboard)/projects/(list)}/_components/project-card.tsx (100%) rename app/{home => (dashboard)/projects/(list)}/_components/project-list.tsx (100%) rename app/{home => (dashboard)/projects/(list)}/_components/status-config.ts (100%) rename app/{home => (dashboard)/projects/(list)}/layout.tsx (100%) rename app/{home => (dashboard)/projects/(list)}/page.tsx (100%) delete mode 100644 app/(dashboard)/projects/page.tsx delete mode 100644 components/features/projectList/EmptyProjectCard.tsx delete mode 100644 components/features/projectList/ProjectCard.tsx delete mode 100644 components/features/projectList/ProjectCardDropdown.tsx delete mode 100644 components/features/projectList/ProjectCardSkeleton.tsx delete mode 100644 components/features/projectList/ProjectListContent.tsx delete mode 100644 components/features/projectList/ProjectListHeader.tsx diff --git a/app/home/_components/create-project-card.tsx b/app/(dashboard)/projects/(list)/_components/create-project-card.tsx similarity index 100% rename from app/home/_components/create-project-card.tsx rename to app/(dashboard)/projects/(list)/_components/create-project-card.tsx diff --git a/app/home/_components/home-page-content.tsx b/app/(dashboard)/projects/(list)/_components/home-page-content.tsx similarity index 67% rename from app/home/_components/home-page-content.tsx rename to app/(dashboard)/projects/(list)/_components/home-page-content.tsx index e285eb0..54534cf 100644 --- a/app/home/_components/home-page-content.tsx +++ b/app/(dashboard)/projects/(list)/_components/home-page-content.tsx @@ -1,20 +1,32 @@ 'use client' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { ProjectStatus } from '@prisma/client' +import { useRouter } from 'next/navigation' import type { ProjectWithRelations } from '@/lib/data/project' import { PageHeaderWithFilter } from './page-header-with-filter' import { ProjectList } from './project-list' +const REFRESH_INTERVAL_MS = 3000 + interface HomePageContentProps { projects: ProjectWithRelations<{ sandboxes: true }>[] } export function HomePageContent({ projects }: HomePageContentProps) { + const router = useRouter() const [activeFilter, setActiveFilter] = useState<'ALL' | ProjectStatus>('ALL') + useEffect(() => { + const interval = setInterval(() => { + router.refresh() + }, REFRESH_INTERVAL_MS) + + return () => clearInterval(interval) + }, [router]) + return ( <> diff --git a/app/home/_components/page-header-with-filter.tsx b/app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx similarity index 100% rename from app/home/_components/page-header-with-filter.tsx rename to app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx diff --git a/app/home/_components/project-actions-menu.tsx b/app/(dashboard)/projects/(list)/_components/project-actions-menu.tsx similarity index 98% rename from app/home/_components/project-actions-menu.tsx rename to app/(dashboard)/projects/(list)/_components/project-actions-menu.tsx index fa90fee..a831531 100644 --- a/app/home/_components/project-actions-menu.tsx +++ b/app/(dashboard)/projects/(list)/_components/project-actions-menu.tsx @@ -177,7 +177,7 @@ export function ProjectActionsMenu({ projectId, projectName, status }: ProjectAc {/* Confirmation Input */}
- {/* Header Bar */} - - - {/* Content */} - -
- ); -} diff --git a/components/features/projectList/EmptyProjectCard.tsx b/components/features/projectList/EmptyProjectCard.tsx deleted file mode 100644 index 222a0e9..0000000 --- a/components/features/projectList/EmptyProjectCard.tsx +++ /dev/null @@ -1,33 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { MdAdd } from 'react-icons/md'; - -import CreateProjectDialog from '@/components/dialog/create-project-dialog'; -import { Card } from '@/components/ui/card'; -import { cn } from '@/lib/utils'; - -export default function EmptyProjectCard() { - const [createModalOpen, setCreateModalOpen] = useState(false); - - return ( - <> - setCreateModalOpen(true)} - > -
- -
- Create new project -
- - - - ); -} diff --git a/components/features/projectList/ProjectCard.tsx b/components/features/projectList/ProjectCard.tsx deleted file mode 100644 index fbb4692..0000000 --- a/components/features/projectList/ProjectCard.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * ProjectCard Component - * - * Displays a single project card with status, metadata, and action menu - */ - -import { memo } from 'react'; -import { MdAccessTime } from 'react-icons/md'; -import type { Prisma } from '@prisma/client'; -import Link from 'next/link'; - -import { Badge } from '@/components/ui/badge'; -import { - Card, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { getStatusBgClasses, getStatusTextColor } from '@/lib/util/status-colors'; -import { cn } from '@/lib/utils'; - -import ProjectCardDropdown from './ProjectCardDropdown'; - -type ProjectWithResources = Prisma.ProjectGetPayload<{ - include: { - sandboxes: true; - databases: true; - environments: true; - }; -}>; - -interface ProjectCardProps { - project: ProjectWithResources; -} - -const ProjectCard = memo(({ project }: ProjectCardProps) => { - return ( - - - -
-
- - {project.name} - -
- -
- - - {project.description || 'No description'} - -
- - {/* Horizontal divider matching content width */} -
- - -
- - - {project.status} - -
- -
- - - {new Date(project.updatedAt).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - })} - -
-
- - - ); -}); - -ProjectCard.displayName = 'ProjectCard'; - -export default ProjectCard; \ No newline at end of file diff --git a/components/features/projectList/ProjectCardDropdown.tsx b/components/features/projectList/ProjectCardDropdown.tsx deleted file mode 100644 index e698e27..0000000 --- a/components/features/projectList/ProjectCardDropdown.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/** - * ProjectCardDropdown Component - * - * Dropdown menu for project operations (Start/Stop/Delete) - * Includes delete confirmation dialog - */ - -'use client'; - -import { useState } from 'react'; -import { MdDeleteOutline, MdMoreVert, MdPlayArrow, MdRefresh, MdStop } from 'react-icons/md'; -import type { Prisma } from '@prisma/client'; - -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog'; -import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { useProjectOperations } from '@/hooks/use-project-operations'; -import { getAvailableProjectActions } from '@/lib/util/action'; - -type ProjectWithResources = Prisma.ProjectGetPayload<{ - include: { - sandboxes: true; - databases: true; - }; -}>; - -interface ProjectCardDropdownProps { - project: ProjectWithResources; -} - -export default function ProjectCardDropdown({ project }: ProjectCardDropdownProps) { - const [showDeleteDialog, setShowDeleteDialog] = useState(false); - const { executeOperation, loading } = useProjectOperations(project.id); - const availableActions = getAvailableProjectActions(project); - - const handleDeleteClick = () => { - setShowDeleteDialog(true); - }; - - const handleDeleteConfirm = () => { - setShowDeleteDialog(false); - executeOperation('DELETE'); - }; - - return ( - <> - - - - - - {availableActions.includes('START') && ( - { - e.stopPropagation(); - executeOperation('START'); - }} - disabled={loading !== null} - > - {loading === 'START' ? ( - <> - - Starting... - - ) : ( - <> - - Start - - )} - - )} - {availableActions.includes('STOP') && ( - { - e.stopPropagation(); - executeOperation('STOP'); - }} - disabled={loading !== null} - > - {loading === 'STOP' ? ( - <> - - Stopping... - - ) : ( - <> - - Stop - - )} - - )} - {availableActions.includes('DELETE') && ( - { - e.stopPropagation(); - handleDeleteClick(); - }} - disabled={loading !== null} - className="text-destructive" - > - - Delete - - )} - - - - {/* Delete Confirmation Dialog */} - - - - Are you sure you want to delete {project.name}? - - This will terminate all resources - (databases, sandboxes) and cannot be undone. - - - - Cancel - - Delete - - - - - - ); -} \ No newline at end of file diff --git a/components/features/projectList/ProjectCardSkeleton.tsx b/components/features/projectList/ProjectCardSkeleton.tsx deleted file mode 100644 index 928c054..0000000 --- a/components/features/projectList/ProjectCardSkeleton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Card, CardFooter, CardHeader } from '@/components/ui/card'; -import { Skeleton } from '@/components/ui/skeleton'; - -export default function ProjectCardSkeleton() { - return ( - - -
-
- -
- -
- -
- - -
-
- -
- - -
- - -
- -
- - -
-
- - ); -} diff --git a/components/features/projectList/ProjectListContent.tsx b/components/features/projectList/ProjectListContent.tsx deleted file mode 100644 index 8cbb158..0000000 --- a/components/features/projectList/ProjectListContent.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client'; - - -import EmptyProjectCard from '@/components/features/projectList/EmptyProjectCard'; -import ProjectCard from '@/components/features/projectList/ProjectCard'; -import ProjectCardSkeleton from '@/components/features/projectList/ProjectCardSkeleton'; -import { useProjects } from '@/hooks/use-projects'; - -export default function ProjectListContent() { - // Fetch projects with automatic polling (every 3 seconds) - // Namespace is automatically determined from user's kubeconfig - const { data: projects, isLoading } = useProjects(); - - if (isLoading) { - return ( -
-
- {Array.from({ length: 3 }).map((_, i) => ( - - ))} -
-
- ); - } - - return ( -
-
- {projects?.map((project) => ( - - ))} - -
-
- ); -} diff --git a/components/features/projectList/ProjectListHeader.tsx b/components/features/projectList/ProjectListHeader.tsx deleted file mode 100644 index d7016d3..0000000 --- a/components/features/projectList/ProjectListHeader.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/** - * ProjectListHeader Component - * - * Header bar for projects page with avatar and settings - */ - -'use client'; - -import { useState } from 'react'; -import { MdAdd, MdSettings } from 'react-icons/md'; - -import CreateProjectDialog from '@/components/dialog/create-project-dialog'; -import SettingsDialog from '@/components/dialog/settings-dialog'; -import { Button } from '@/components/ui/button'; - -const ProjectListHeader = () => { - const [showSettings, setShowSettings] = useState(false); - const [showCreateProject, setShowCreateProject] = useState(false); - - return ( - <> -
-
-

Projects

-

Manage your full-stack applications

-
-
- - - {/* Avatar / Settings Button */} - -
-
- - {/* Create Project Dialog */} - - - {/* Settings Dialog */} - - - ); -}; - -ProjectListHeader.displayName = 'ProjectListHeader'; - -export default ProjectListHeader; diff --git a/package.json b/package.json index f8a70eb..4ec58d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fulling", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.4", "description": "AI-Powered Full-Stack Development Platform", "author": "fanux", "license": "MIT", @@ -70,6 +70,7 @@ "@types/react": "19.2.7", "@types/react-dom": "19.2.3", "@types/ws": "^8.18.1", + "baseline-browser-mapping": "^2.10.0", "eslint": "^9", "eslint-config-next": "16.0.7", "eslint-config-prettier": "^10.1.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d38aec..b120a58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,9 @@ importers: '@types/ws': specifier: ^8.18.1 version: 8.18.1 + baseline-browser-mapping: + specifier: ^2.10.0 + version: 2.10.0 eslint: specifier: ^9 version: 9.39.2(jiti@2.6.1) @@ -1905,8 +1908,9 @@ packages: bare-url@2.3.2: resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} - baseline-browser-mapping@2.9.8: - resolution: {integrity: sha512-Y1fOuNDowLfgKOypdc9SPABfoWXuZHBOyCS4cD52IeZBhr4Md6CLLs6atcxVrzRmQ06E7hSlm5bHHApPKR/byA==} + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} hasBin: true bcryptjs@3.0.3: @@ -5350,7 +5354,7 @@ snapshots: bare-path: 3.0.0 optional: true - baseline-browser-mapping@2.9.8: {} + baseline-browser-mapping@2.10.0: {} bcryptjs@3.0.3: {} @@ -5369,7 +5373,7 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.8 + baseline-browser-mapping: 2.10.0 caniuse-lite: 1.0.30001760 electron-to-chromium: 1.5.267 node-releases: 2.0.27