-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): make workspace page server rendered [2024-11-02]
- Loading branch information
Showing
4 changed files
with
49 additions
and
53 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,67 @@ | ||
'use client' | ||
import React, { useEffect, useState } from 'react'; | ||
// layout.tsx | ||
import React from 'react'; | ||
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; | ||
import { AppSidebar } from "./workspace-sidebar"; | ||
import { Menu } from "lucide-react"; | ||
import { cookies } from 'next/headers'; | ||
|
||
export default function Layout({ children }: { children: React.ReactNode }) { | ||
const [profileData, setProfileData] = useState({ | ||
first_name: '', | ||
email: '[email protected]', // fallback default | ||
avatar_url: '/path/to/avatar.jpg' // fallback default | ||
}); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [error, setError] = useState<string | null>(null); | ||
// Define the profile data type | ||
type ProfileData = { | ||
first_name: string; | ||
email: string; | ||
}; | ||
|
||
useEffect(() => { | ||
const fetchProfile = async () => { | ||
try { | ||
setIsLoading(true); | ||
const response = await fetch('/api/profile'); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to fetch profile'); | ||
} | ||
|
||
const data = await response.json(); | ||
setProfileData({ | ||
first_name: data.first_name, | ||
email: data.email || profileData.email, | ||
avatar_url: data.avatar_url || profileData.avatar_url | ||
}); | ||
} catch (error) { | ||
console.error('Error fetching profile:', error); | ||
setError(error instanceof Error ? error.message : 'Failed to load profile'); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
// Server action to fetch profile data | ||
async function getProfile(): Promise<ProfileData> { | ||
try { | ||
// You can access cookies or headers here if needed | ||
const cookieStore = await cookies(); | ||
const sessionId = cookieStore.get("sid").value; | ||
|
||
fetchProfile(); | ||
}, []); // Empty dependency array means this runs once on mount | ||
const response = await fetch(`${process.env.GRIDWALK_API}/profile`, { | ||
headers: { | ||
'Authorization': `Bearer ${sessionId}` | ||
}, | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to fetch profile'); | ||
} | ||
|
||
const data = await response.json(); | ||
|
||
return { | ||
first_name: data.first_name, | ||
email: data.email || '[email protected]', | ||
}; | ||
} catch (error) { | ||
console.error('Error fetching profile:', error); | ||
// Return default values if fetch fails | ||
return { | ||
first_name: '', | ||
email: '', | ||
}; | ||
} | ||
} | ||
|
||
// Mark the component as async | ||
export default async function Layout({ children }: { children: React.ReactNode }) { | ||
// Fetch profile data on the server | ||
const profileData = await getProfile(); | ||
|
||
return ( | ||
<SidebarProvider> | ||
<div className="flex h-screen"> | ||
<AppSidebar | ||
userName={profileData.first_name} | ||
userEmail={profileData.email} | ||
avatarUrl={profileData.avatar_url} | ||
/> | ||
<main className="flex-1 px-4 overflow-auto bg-gray-50"> | ||
<div className="p-4"> | ||
<SidebarTrigger className="lg:hidden"> | ||
<Menu className="h-5 w-5" /> | ||
</SidebarTrigger> | ||
{isLoading ? ( | ||
<div>Loading profile...</div> | ||
) : error ? ( | ||
<div>Error: {error}</div> | ||
) : ( | ||
children | ||
)} | ||
{children} | ||
</div> | ||
</main> | ||
</div> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,21 +6,16 @@ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; | |
interface AppSidebarProps { | ||
userName?: string; | ||
userEmail?: string; | ||
avatarUrl?: string; | ||
} | ||
|
||
const navItems = [ | ||
{ icon: Map, label: 'Projects', href: '/workspace' }, | ||
{ icon: Database, label: 'Connections', href: '/workspace' }, | ||
]; | ||
|
||
export function AppSidebar({ | ||
userName = "John Doe", | ||
userEmail = "[email protected]", | ||
avatarUrl = "/api/placeholder/32/32" | ||
}: AppSidebarProps) { | ||
export function AppSidebar({userName, userEmail, avatarUrl}: AppSidebarProps) { | ||
// Get first letter of name for avatar fallback | ||
const avatarFallback = userName.charAt(0).toUpperCase(); | ||
const avatar = userName.charAt(0).toUpperCase(); | ||
|
||
return ( | ||
<Sidebar className=""> | ||
|
@@ -49,8 +44,7 @@ export function AppSidebar({ | |
<div className="border-t p-4"> | ||
<div className="flex items-center gap-3"> | ||
<Avatar> | ||
<AvatarImage src={avatarUrl} alt={userName} /> | ||
<AvatarFallback>{avatarFallback}</AvatarFallback> | ||
<AvatarFallback>{avatar}</AvatarFallback> | ||
</Avatar> | ||
<div> | ||
<p className="text-sm font-medium">{userName}</p> | ||
|