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