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',