Skip to content

Commit 8ae15b2

Browse files
authored
fix: use client-side polling for real-time project status updates (#118)
Components were stuck showing "CREATING" status because they received static server-rendered props from layout.tsx that never updated after initial page load. Changes: - StatusBar: Convert to client component, use useProject hook - ProjectSidebar: Use useProject hook instead of static props - ProjectContentWrapper: Use useProject hook for TerminalContainer - layout.tsx: Pass projectId instead of full project object, simplify Prisma query to only check project existence All components now use useProject hook with 3s polling interval for real-time status updates, matching PrimarySidebar behavior.
1 parent 4de4b53 commit 8ae15b2

5 files changed

Lines changed: 49 additions & 95 deletions

File tree

app/projects/[id]/layout.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ export default async function ProjectLayout({
2323

2424
const { id } = await params;
2525

26+
// Only need to check if project exists and belongs to user
27+
// All components fetch their own data via useProject hook
2628
const project = await prisma.project.findFirst({
2729
where: {
2830
id: id,
2931
userId: session.user.id,
3032
},
31-
include: {
32-
sandboxes: true,
33-
databases: true,
34-
environments: true,
33+
select: {
34+
id: true,
3535
},
3636
});
3737

@@ -48,20 +48,18 @@ export default async function ProjectLayout({
4848

4949
{/* Secondary Sidebar - Project Settings */}
5050
<ProjectSidebar
51-
project={project}
52-
sandboxes={project.sandboxes}
53-
envVars={project.environments}
51+
projectId={id}
5452
/>
5553

5654
{/* Main Content Area */}
5755
<div className="flex-1 flex flex-col min-w-0 relative overflow-hidden">
58-
<ProjectContentWrapper project={project} sandbox={project.sandboxes[0]}>
56+
<ProjectContentWrapper projectId={id}>
5957
{children}
6058
</ProjectContentWrapper>
6159
</div>
6260
</div>
6361

64-
<StatusBar project={project} />
62+
<StatusBar projectId={id} />
6563
</div>
6664
);
6765
}

app/projects/[id]/loading.tsx

Lines changed: 0 additions & 48 deletions
This file was deleted.

components/layout/project-content-wrapper.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@
2020

2121
import { usePathname } from 'next/navigation';
2222

23-
import { TerminalContainer, type TerminalContainerProps } from '@/components/terminal/terminal-container';
23+
import { TerminalContainer } from '@/components/terminal/terminal-container';
24+
import { useProject } from '@/hooks/use-project';
2425

2526
import styles from './project-content-wrapper.module.css';
2627

2728
// ============================================================================
2829
// Types
2930
// ============================================================================
3031

31-
interface ProjectContentWrapperProps extends TerminalContainerProps {
32+
interface ProjectContentWrapperProps {
33+
projectId: string;
3234
children: React.ReactNode;
3335
}
3436

@@ -38,14 +40,17 @@ interface ProjectContentWrapperProps extends TerminalContainerProps {
3840

3941
export function ProjectContentWrapper({
4042
children,
41-
project,
42-
sandbox,
43+
projectId,
4344
}: ProjectContentWrapperProps) {
45+
const { data: project } = useProject(projectId);
4446
const pathname = usePathname();
4547

4648
// Determine which panel to display based on current route
4749
const isTerminalPage = pathname?.endsWith('/terminal') ?? false;
4850

51+
// Get sandbox from project data
52+
const sandbox = project?.sandboxes?.[0];
53+
4954
return (
5055
<div className={styles.wrapper}>
5156
{/* Terminal Panel - Persisted across navigation */}
@@ -57,11 +62,13 @@ export function ProjectContentWrapper({
5762
role="region"
5863
aria-label="Terminal Console"
5964
>
60-
<TerminalContainer
61-
project={project}
62-
sandbox={sandbox}
63-
isVisible={isTerminalPage}
64-
/>
65+
{project && (
66+
<TerminalContainer
67+
project={project}
68+
sandbox={sandbox}
69+
isVisible={isTerminalPage}
70+
/>
71+
)}
6572
</div>
6673

6774
{/* Content Panel - Regular pages (overview, settings, env, etc.) */}

components/layout/status-bar.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
1+
'use client';
2+
13
import React from 'react';
2-
import { Prisma } from '@prisma/client';
34
import { Box, Database } from 'lucide-react';
45

56
import { RepoStatusIndicator } from '@/components/layout/repo-status-indicator';
7+
import { useProject } from '@/hooks/use-project';
68
import { getStatusIconColor } from '@/lib/util/status-colors';
79

8-
type ProjectWithRelations = Prisma.ProjectGetPayload<{
9-
include: {
10-
sandboxes: true;
11-
databases: true;
12-
environments: true;
13-
};
14-
}>;
15-
1610
interface StatusBarProps {
17-
project: ProjectWithRelations;
11+
projectId: string;
1812
}
1913

20-
export function StatusBar({ project }: StatusBarProps) {
21-
const database = project.databases?.[0];
14+
export function StatusBar({ projectId }: StatusBarProps) {
15+
const { data: project } = useProject(projectId);
16+
17+
const database = project?.databases?.[0];
2218
const dbStatus = database?.status || 'CREATING';
23-
const sandbox = project.sandboxes?.[0];
19+
const sandbox = project?.sandboxes?.[0];
2420
const sbStatus = sandbox?.status || 'CREATING';
2521

2622
return (
2723
<div className="h-6 bg-primary text-card-foreground [&_span]:text-card-foreground flex items-center justify-between px-2 text-xs select-none z-50">
2824
<div className="flex items-center gap-4">
29-
<RepoStatusIndicator
30-
project={project}
31-
/>
25+
{project && (
26+
<RepoStatusIndicator
27+
project={project}
28+
/>
29+
)}
3230

3331
</div>
3432

components/sidebars/project-sidebar-new.tsx

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use client';
22

33
import { useState } from 'react';
4-
import { Environment, Project, Sandbox } from '@prisma/client';
54
import {
65
ChevronLeft,
76
ChevronRight,
@@ -16,15 +15,15 @@ import {
1615
import Link from 'next/link';
1716
import { usePathname } from 'next/navigation';
1817

18+
import { useProject } from '@/hooks/use-project';
1919
import { cn } from '@/lib/utils';
2020

2121
interface ProjectSidebarProps {
22-
project: Project;
23-
sandboxes: Sandbox[];
24-
envVars: Environment[];
22+
projectId: string;
2523
}
2624

27-
export default function ProjectSidebar({ project }: ProjectSidebarProps) {
25+
export default function ProjectSidebar({ projectId }: ProjectSidebarProps) {
26+
const { data: project } = useProject(projectId);
2827
const [isCollapsed, setIsCollapsed] = useState(false);
2928
const pathname = usePathname();
3029

@@ -33,36 +32,36 @@ export default function ProjectSidebar({ project }: ProjectSidebarProps) {
3332
id: 'terminal',
3433
label: 'Web Terminal',
3534
icon: Terminal,
36-
href: `/projects/${project.id}/terminal`,
35+
href: `/projects/${projectId}/terminal`,
3736
},
38-
{ id: 'database', label: 'Database', icon: Database, href: `/projects/${project.id}/database` },
37+
{ id: 'database', label: 'Database', icon: Database, href: `/projects/${projectId}/database` },
3938
];
4039

4140
const configSections = [
4241
{
4342
id: 'environment',
4443
label: 'Environment Variables',
4544
icon: Package,
46-
href: `/projects/${project.id}/environment`,
45+
href: `/projects/${projectId}/environment`,
4746
},
4847
{
4948
id: 'secrets',
5049
label: 'Secret Configuration',
5150
icon: Key,
52-
href: `/projects/${project.id}/secrets`,
51+
href: `/projects/${projectId}/secrets`,
5352
},
54-
{ id: 'auth', label: 'Auth Configuration', icon: Shield, href: `/projects/${project.id}/auth` },
53+
{ id: 'auth', label: 'Auth Configuration', icon: Shield, href: `/projects/${projectId}/auth` },
5554
{
5655
id: 'payment',
5756
label: 'Payment Configuration',
5857
icon: CreditCard,
59-
href: `/projects/${project.id}/payment`,
58+
href: `/projects/${projectId}/payment`,
6059
},
6160
{
6261
id: 'github',
6362
label: 'GitHub Integration',
6463
icon: Github,
65-
href: `/projects/${project.id}/github`,
64+
href: `/projects/${projectId}/github`,
6665
},
6766
];
6867

@@ -76,7 +75,7 @@ export default function ProjectSidebar({ project }: ProjectSidebarProps) {
7675
{/* Header */}
7776
<div className="h-12 flex items-center justify-between px-3 border-b border-border">
7877
{!isCollapsed && (
79-
<span className="text-sm font-medium text-foreground">Project {project.name}</span>
78+
<span className="text-sm font-medium text-foreground">Project {project?.name ?? 'Loading...'}</span>
8079
)}
8180
<button
8281
onClick={() => setIsCollapsed(!isCollapsed)}

0 commit comments

Comments
 (0)