Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions app/dashboard/company/[slug]/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
'use client'

import React, { useState, useEffect } from 'react'
import { useCompanyContext } from '@/contexts/CompanyContext'
import { usePendingInvitationRedirect } from '@/lib/hooks/usePendingInvitationRedirect'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Separator } from '@/components/ui/separator'
import { Badge } from '@/components/ui/badge'
import {
AlertCircle,
Loader2,
Mail,
Phone,
User,
Building2,
} from 'lucide-react'
import { useToast } from '@/components/ui/use-toast'
import { createClient } from '@/lib/supabase/client'

interface UserProfile {
id: string
email: string
first_name: string | null
last_name: string | null
phone: string | null
bio: string | null
current_position: string | null
company: string | null
location: string | null
github_url: string | null
linkedin_url: string | null
twitter_url: string | null
avatar_url: string | null
}

export default function CompanyProfilePage() {
const { currentCompany, userRole, loading: contextLoading } = useCompanyContext()
const isPendingInvitation = usePendingInvitationRedirect()
const { toast } = useToast()
const [loading, setLoading] = useState(true)
const [profile, setProfile] = useState<UserProfile | null>(null)

const [formData, setFormData] = useState({
first_name: '',
last_name: '',
phone: '',
})

useEffect(() => {
loadProfile()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const loadProfile = async () => {
try {
const supabase = createClient()
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
toast({
title: 'Error',
description: 'You must be logged in to view this page',
variant: 'destructive',
})
return
}

const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single()

if (error) throw error

setProfile(data)
setFormData({
first_name: data.first_name || '',
last_name: data.last_name || '',
phone: data.phone || '',
})
} catch (error) {
console.error('Error loading profile:', error)
toast({
title: 'Error',
description: 'Failed to load profile',
variant: 'destructive',
})
} finally {
setLoading(false)
}
}



if (contextLoading || isPendingInvitation || loading) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
)
}

if (!profile) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<Alert variant="destructive" className="max-w-md">
<AlertCircle className="h-4 w-4" />
<AlertDescription>Profile not found</AlertDescription>
</Alert>
</div>
)
}

return (
<div className="space-y-6">
{/* Header */}
<div>
<h1 className="text-3xl font-bold tracking-tight text-white">My Profile</h1>
<p className="text-muted-foreground">
View your personal information and company details
</p>
</div>

{/* Personal Information - Read Only */}
<Card className="bg-zinc-900 border-zinc-800">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<User className="h-5 w-5 text-purple-400" />
Personal Information
</CardTitle>
<CardDescription>
Your profile details (read-only)
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid gap-4 md:grid-cols-2">
<div className="space-y-2">
<Label className="text-zinc-200">First Name</Label>
<Input
value={formData.first_name}
disabled
className="bg-zinc-800 border-zinc-700 text-zinc-400"
/>
</div>
<div className="space-y-2">
<Label className="text-zinc-200">Last Name</Label>
<Input
value={formData.last_name}
disabled
className="bg-zinc-800 border-zinc-700 text-zinc-400"
/>
</div>
</div>

<div className="space-y-2">
<Label className="text-zinc-200 flex items-center gap-2">
<Mail className="h-4 w-4 text-purple-400" />
Email
</Label>
<Input
type="email"
value={profile.email}
disabled
className="bg-zinc-800 border-zinc-700 text-zinc-400"
/>
</div>

<div className="space-y-2">
<Label className="text-zinc-200 flex items-center gap-2">
<Phone className="h-4 w-4 text-purple-400" />
Phone Number
</Label>
<Input
type="tel"
value={formData.phone || 'Not provided'}
disabled
className="bg-zinc-800 border-zinc-700 text-zinc-400"
/>
</div>

{currentCompany && (
<>
<Separator className="bg-zinc-800 my-4" />

<div className="space-y-3">
<Label className="text-zinc-200 flex items-center gap-2">
<Building2 className="h-4 w-4 text-purple-400" />
Company Context
</Label>

<div className="flex items-center justify-between p-3 bg-zinc-800/50 rounded-lg border border-zinc-700">
<span className="text-zinc-400 text-sm">Company</span>
<span className="text-white font-medium">{currentCompany.name}</span>
</div>

<div className="flex items-center justify-between p-3 bg-zinc-800/50 rounded-lg border border-zinc-700">
<span className="text-zinc-400 text-sm">Your Role</span>
<Badge variant="secondary" className="capitalize bg-purple-600/20 text-purple-300 border-purple-600/30">
{userRole}
</Badge>
</div>
</div>
</>
)}
</CardContent>
</Card>

<Alert className="bg-zinc-900 border-zinc-800">
<AlertCircle className="h-4 w-4 text-purple-400" />
<AlertDescription className="text-zinc-300">
Profile information is currently read-only. To update your details, please contact your administrator or support team.
</AlertDescription>
</Alert>
</div>
)
}
6 changes: 4 additions & 2 deletions app/dashboard/company/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CreditCard,
} from 'lucide-react'
import { useAuth } from '@/lib/hooks/useAuth'
import { useRoleProtection } from '@/lib/hooks/useRoleProtection'

export type SidebarGroupType = {
title: string
Expand All @@ -33,6 +34,7 @@ export default function CompanyDashboardLayout({
children: React.ReactNode
}) {
const { user, loading, error } = useAuth()
const { isChecking, isAuthorized } = useRoleProtection('company_member')
const params = useParams()
const companySlug = params?.slug as string | undefined

Expand All @@ -51,7 +53,7 @@ export default function CompanyDashboardLayout({
)
}

if (loading) {
if (loading || isChecking) {
return (
<div className="flex items-center justify-center min-h-screen bg-black">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
Expand All @@ -75,7 +77,7 @@ export default function CompanyDashboardLayout({
)
}

if (!user) {
if (!user || !isAuthorized) {
return (
<div className="flex items-center justify-center min-h-screen px-4 bg-black">
<div className="text-center max-w-md">
Expand Down
6 changes: 4 additions & 2 deletions app/protected/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
Star,
} from "lucide-react"
import { useAuth } from "@/lib/hooks/useAuth"
import { useRoleProtection } from "@/lib/hooks/useRoleProtection"

export type SidebarGroupType = {
title: string;
Expand Down Expand Up @@ -210,16 +211,17 @@ const sidebarItems: SidebarGroupType[] = [

export default function ProtectedLayout({ children }: { children: React.ReactNode }) {
const { user, loading } = useAuth()
const { isChecking, isAuthorized } = useRoleProtection('student')

if (loading) {
if (loading || isChecking) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
)
}

if (!user) {
if (!user || !isAuthorized) {
return (
<div className="flex items-center justify-center min-h-screen px-4">
<div className="text-center max-w-md">
Expand Down
29 changes: 2 additions & 27 deletions components/dashboard/CompanySidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useSafeNavigation } from '@/lib/security/safe-navigation'
import {
Bell,
LogOut,
User,
ChevronDown,
Expand Down Expand Up @@ -194,23 +193,11 @@ export function CompanySidebar({
asChild
className="flex items-center gap-2 px-3 py-2 hover:bg-purple-700/10 rounded-md cursor-pointer"
>
<Link href="/protected/profile" className="flex items-center w-full">
<Link href={`/dashboard/company/${currentCompany?.slug}/profile`} className="flex items-center w-full">
<User className="size-4 text-purple-400" />
<span>My Profile</span>
</Link>
</DropdownMenuItem>
<DropdownMenuItem
asChild
className="flex items-center gap-2 px-3 py-2 hover:bg-purple-700/10 rounded-md cursor-pointer"
>
<Link
href="/protected/notifications"
className="flex items-center w-full"
>
<Bell className="size-4 text-purple-400" />
<span>Notifications</span>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => navigateTo('/auth/signin')}
Expand Down Expand Up @@ -452,23 +439,11 @@ export function CompanySidebar({
asChild
className="flex items-center gap-2 px-3 py-2 hover:bg-purple-700/10 rounded-md cursor-pointer"
>
<Link href="/protected/profile" className="flex items-center w-full">
<Link href={`/dashboard/company/${currentCompany?.slug}/profile`} className="flex items-center w-full">
<User className="size-4 text-purple-400" />
<span>My Profile</span>
</Link>
</DropdownMenuItem>
<DropdownMenuItem
asChild
className="flex items-center gap-2 px-3 py-2 hover:bg-purple-700/10 rounded-md cursor-pointer"
>
<Link
href="/protected/notifications"
className="flex items-center w-full"
>
<Bell className="size-4 text-purple-400" />
<span>Notifications</span>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => navigateTo('/auth/signin')}
Expand Down
Loading
Loading