From 609764699d39baeae54bf482d2ebb791e56f4ad5 Mon Sep 17 00:00:00 2001
From: Che <30403707+Che-Zhu@users.noreply.github.com>
Date: Wed, 11 Mar 2026 15:31:00 +0800
Subject: [PATCH 1/2] Refine project status display for imports
---
.../(list)/_components/home-page-content.tsx | 4 +-
.../_components/page-header-with-filter.tsx | 7 +-
.../(list)/_components/project-card.tsx | 7 +-
.../(list)/_components/project-list.tsx | 13 ++--
.../(list)/_components/status-config.ts | 18 +++--
lib/util/project-display-status.ts | 75 +++++++++++++++++++
6 files changed, 106 insertions(+), 18 deletions(-)
create mode 100644 lib/util/project-display-status.ts
diff --git a/app/(dashboard)/projects/(list)/_components/home-page-content.tsx b/app/(dashboard)/projects/(list)/_components/home-page-content.tsx
index dedaa67..ffe161c 100644
--- a/app/(dashboard)/projects/(list)/_components/home-page-content.tsx
+++ b/app/(dashboard)/projects/(list)/_components/home-page-content.tsx
@@ -1,13 +1,13 @@
'use client'
import { useEffect, useState } from 'react'
-import { ProjectStatus } from '@prisma/client'
import { useRouter, useSearchParams } from 'next/navigation'
import { toast } from 'sonner'
import { getInstallations } from '@/lib/actions/github'
import type { ProjectWithRelations } from '@/lib/data/project'
import { env } from '@/lib/env'
+import type { ProjectDisplayStatus } from '@/lib/util/project-display-status'
import { PageHeaderWithFilter } from './page-header-with-filter'
import { ProjectList } from './project-list'
@@ -21,7 +21,7 @@ interface HomePageContentProps {
export function HomePageContent({ projects }: HomePageContentProps) {
const router = useRouter()
const searchParams = useSearchParams()
- const [activeFilter, setActiveFilter] = useState<'ALL' | ProjectStatus>('ALL')
+ const [activeFilter, setActiveFilter] = useState<'ALL' | ProjectDisplayStatus>('ALL')
const [hasTriggeredInstall, setHasTriggeredInstall] = useState(false)
useEffect(() => {
diff --git a/app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx b/app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx
index e98be47..679aad9 100644
--- a/app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx
+++ b/app/(dashboard)/projects/(list)/_components/page-header-with-filter.tsx
@@ -1,11 +1,10 @@
'use client'
-import { ProjectStatus } from '@prisma/client'
-
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
+import type { ProjectDisplayStatus } from '@/lib/util/project-display-status'
import { cn } from '@/lib/utils'
-type FilterStatus = 'ALL' | ProjectStatus
+type FilterStatus = 'ALL' | ProjectDisplayStatus
interface PageHeaderWithFilterProps {
activeFilter: FilterStatus
@@ -15,7 +14,9 @@ interface PageHeaderWithFilterProps {
const filters: { label: string; value: FilterStatus }[] = [
{ label: 'All', value: 'ALL' },
{ label: 'Running', value: 'RUNNING' },
+ { label: 'Importing', value: 'IMPORTING' },
{ label: 'Stopped', value: 'STOPPED' },
+ { label: 'Needs Attention', value: 'NEEDS_ATTENTION' },
]
export function PageHeaderWithFilter({ activeFilter, onFilterChange }: PageHeaderWithFilterProps) {
diff --git a/app/(dashboard)/projects/(list)/_components/project-card.tsx b/app/(dashboard)/projects/(list)/_components/project-card.tsx
index 0c52abb..4e347cc 100644
--- a/app/(dashboard)/projects/(list)/_components/project-card.tsx
+++ b/app/(dashboard)/projects/(list)/_components/project-card.tsx
@@ -4,6 +4,7 @@ import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader } from '@/components/ui/card'
+import type { ProjectDisplayStatus } from '@/lib/util/project-display-status'
import { cn } from '@/lib/utils'
import { ProjectActionsMenu } from './project-actions-menu'
@@ -14,6 +15,7 @@ interface ProjectCardProps {
name: string
description: string
status: ProjectStatus
+ displayStatus: ProjectDisplayStatus
updatedAt: string
publicUrl?: string | null
}
@@ -24,10 +26,11 @@ export function ProjectCard({
name,
description,
status,
+ displayStatus,
updatedAt,
publicUrl,
}: ProjectCardProps) {
- const config = statusConfig[status]
+ const config = statusConfig[displayStatus]
const initial = name.charAt(0).toUpperCase()
// Handle open project button - open sandbox publicUrl in new tab
@@ -95,7 +98,7 @@ export function ProjectCard({
{/* Status indicator */}
- {status === 'RUNNING' && (
+ {displayStatus === 'RUNNING' && (
[]
- activeFilter: 'ALL' | ProjectStatus
+ activeFilter: 'ALL' | ProjectDisplayStatus
}
export function ProjectList({ projects, activeFilter }: ProjectListProps) {
@@ -18,6 +20,7 @@ export function ProjectList({ projects, activeFilter }: ProjectListProps) {
name: p.name,
description: p.description || 'No description',
status: p.status,
+ displayStatus: getProjectDisplayStatus(p),
updatedAt: formatRelativeTime(p.updatedAt),
publicUrl: p.sandboxes?.[0]?.publicUrl,
}))
@@ -25,7 +28,7 @@ export function ProjectList({ projects, activeFilter }: ProjectListProps) {
const filteredProjects =
activeFilter === 'ALL'
? mappedProjects
- : mappedProjects.filter((p) => p.status === activeFilter)
+ : mappedProjects.filter((p) => p.displayStatus === activeFilter)
return (
@@ -35,4 +38,4 @@ export function ProjectList({ projects, activeFilter }: ProjectListProps) {
)
-}
\ No newline at end of file
+}
diff --git a/app/(dashboard)/projects/(list)/_components/status-config.ts b/app/(dashboard)/projects/(list)/_components/status-config.ts
index 0979a2c..4ae8596 100644
--- a/app/(dashboard)/projects/(list)/_components/status-config.ts
+++ b/app/(dashboard)/projects/(list)/_components/status-config.ts
@@ -1,4 +1,4 @@
-import { ProjectStatus } from '@prisma/client'
+import type { ProjectDisplayStatus } from '@/lib/util/project-display-status'
export interface StatusConfigItem {
color: string
@@ -7,7 +7,7 @@ export interface StatusConfigItem {
animate?: string
}
-export const statusConfig: Record = {
+export const statusConfig: Record = {
// Stable states
RUNNING: {
color: 'text-emerald-500',
@@ -32,6 +32,12 @@ export const statusConfig: Record = {
label: 'Creating',
animate: 'animate-pulse',
},
+ IMPORTING: {
+ color: 'text-sky-400',
+ bg: 'bg-sky-400',
+ label: 'Importing',
+ animate: 'animate-pulse',
+ },
UPDATING: {
color: 'text-blue-500',
bg: 'bg-blue-500',
@@ -62,9 +68,9 @@ export const statusConfig: Record = {
bg: 'bg-red-500',
label: 'Error',
},
- PARTIAL: {
- color: 'text-purple-500',
- bg: 'bg-purple-500',
- label: 'Partial',
+ NEEDS_ATTENTION: {
+ color: 'text-amber-400',
+ bg: 'bg-amber-400',
+ label: 'Needs Attention',
},
}
diff --git a/lib/util/project-display-status.ts b/lib/util/project-display-status.ts
new file mode 100644
index 0000000..a02e5e5
--- /dev/null
+++ b/lib/util/project-display-status.ts
@@ -0,0 +1,75 @@
+import type { ProjectImportStatus, ProjectStatus } from '@prisma/client'
+
+export type ProjectDisplayStatus =
+ | 'CREATING'
+ | 'IMPORTING'
+ | 'STARTING'
+ | 'RUNNING'
+ | 'STOPPING'
+ | 'STOPPED'
+ | 'UPDATING'
+ | 'TERMINATING'
+ | 'ERROR'
+ | 'NEEDS_ATTENTION'
+
+type ProjectDisplayStatusInput = {
+ status: ProjectStatus
+ importStatus: ProjectImportStatus
+ githubRepoFullName?: string | null
+ githubAppInstallationId?: string | null
+}
+
+export function isImportProject(project: {
+ githubRepoFullName?: string | null
+ githubAppInstallationId?: string | null
+}): boolean {
+ return Boolean(project.githubRepoFullName || project.githubAppInstallationId)
+}
+
+export function getProjectDisplayStatus(
+ project: ProjectDisplayStatusInput
+): ProjectDisplayStatus {
+ const importProject = isImportProject(project)
+
+ if (project.importStatus === 'FAILED') {
+ return 'NEEDS_ATTENTION'
+ }
+
+ if (project.status === 'ERROR') {
+ return 'ERROR'
+ }
+
+ if (project.status === 'TERMINATING') {
+ return 'TERMINATING'
+ }
+
+ if (project.status === 'STOPPING') {
+ return 'STOPPING'
+ }
+
+ if (project.status === 'STOPPED') {
+ return 'STOPPED'
+ }
+
+ if (project.status === 'UPDATING') {
+ return 'UPDATING'
+ }
+
+ if (importProject && (project.importStatus === 'PENDING' || project.importStatus === 'CLONING')) {
+ return 'IMPORTING'
+ }
+
+ if (project.status === 'CREATING') {
+ return 'CREATING'
+ }
+
+ if (project.status === 'STARTING') {
+ return 'STARTING'
+ }
+
+ if (project.status === 'RUNNING') {
+ return 'RUNNING'
+ }
+
+ return 'NEEDS_ATTENTION'
+}
From 016f3469598fd9c76d1f0a1266a1d335cd9c4f54 Mon Sep 17 00:00:00 2001
From: Che <30403707+Che-Zhu@users.noreply.github.com>
Date: Wed, 11 Mar 2026 15:43:03 +0800
Subject: [PATCH 2/2] Fix display status config typing
---
app/(dashboard)/projects/(list)/_components/status-config.ts | 5 -----
1 file changed, 5 deletions(-)
diff --git a/app/(dashboard)/projects/(list)/_components/status-config.ts b/app/(dashboard)/projects/(list)/_components/status-config.ts
index 4ae8596..0cfe4c4 100644
--- a/app/(dashboard)/projects/(list)/_components/status-config.ts
+++ b/app/(dashboard)/projects/(list)/_components/status-config.ts
@@ -20,11 +20,6 @@ export const statusConfig: Record = {
bg: 'bg-gray-500',
label: 'Stopped',
},
- TERMINATED: {
- color: 'text-gray-600',
- bg: 'bg-gray-600',
- label: 'Terminated',
- },
// Transition states
CREATING: {
color: 'text-yellow-500',