Skip to content

Commit d5d4b01

Browse files
authored
Merge pull request #329 from codeunia-dev/feat/company-team-roles-and-invite-flow
Feat/company team roles and invite flow
2 parents 6fddaf3 + ec24f7a commit d5d4b01

File tree

8 files changed

+177
-123
lines changed

8 files changed

+177
-123
lines changed

app/dashboard/company/[slug]/events/page.tsx

Lines changed: 77 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ import {
2525
} from '@/components/ui/alert-dialog'
2626

2727
export default function CompanyEventsPage() {
28-
const { currentCompany, loading: companyLoading } = useCompanyContext()
28+
const { currentCompany, userRole, loading: companyLoading } = useCompanyContext()
2929
const isPendingInvitation = usePendingInvitationRedirect()
3030
const [events, setEvents] = useState<Event[]>([])
3131
const [loading, setLoading] = useState(true)
3232
const [searchTerm, setSearchTerm] = useState('')
3333
const [deletingEventSlug, setDeletingEventSlug] = useState<string | null>(null)
3434

35+
const canManageEvents = userRole && ['owner', 'admin', 'editor'].includes(userRole)
36+
3537
const fetchEvents = useCallback(async () => {
3638
if (!currentCompany) return
3739

@@ -170,15 +172,17 @@ export default function CompanyEventsPage() {
170172
<div>
171173
<h1 className="text-3xl font-bold tracking-tight">Events</h1>
172174
<p className="text-muted-foreground mt-1">
173-
Manage your company&apos;s events and hackathons
175+
{canManageEvents ? "Manage your company's events and hackathons" : "View your company's events"}
174176
</p>
175177
</div>
176-
<Link href="/dashboard/company/events/create">
177-
<Button>
178-
<Plus className="h-4 w-4 mr-2" />
179-
Create Event
180-
</Button>
181-
</Link>
178+
{canManageEvents && (
179+
<Link href={`/dashboard/company/${currentCompany?.slug}/events/create`}>
180+
<Button>
181+
<Plus className="h-4 w-4 mr-2" />
182+
Create Event
183+
</Button>
184+
</Link>
185+
)}
182186
</div>
183187

184188
{/* Stats Cards */}
@@ -239,7 +243,7 @@ export default function CompanyEventsPage() {
239243
<CardHeader>
240244
<CardTitle>All Events ({filteredEvents.length})</CardTitle>
241245
<CardDescription>
242-
View and manage all your events
246+
{canManageEvents ? 'View and manage all your events' : 'View all company events'}
243247
</CardDescription>
244248
</CardHeader>
245249
<CardContent>
@@ -254,10 +258,10 @@ export default function CompanyEventsPage() {
254258
No events found
255259
</h3>
256260
<p className="text-gray-500 dark:text-gray-400 mb-4">
257-
{searchTerm ? 'Try adjusting your search' : 'Get started by creating your first event'}
261+
{searchTerm ? 'Try adjusting your search' : canManageEvents ? 'Get started by creating your first event' : 'No events available yet'}
258262
</p>
259-
{!searchTerm && (
260-
<Link href="/dashboard/company/events/create">
263+
{!searchTerm && canManageEvents && (
264+
<Link href={`/dashboard/company/${currentCompany?.slug}/events/create`}>
261265
<Button>
262266
<Plus className="h-4 w-4 mr-2" />
263267
Create Event
@@ -276,7 +280,7 @@ export default function CompanyEventsPage() {
276280
<TableHead>Approval</TableHead>
277281
<TableHead>Views</TableHead>
278282
<TableHead>Registered</TableHead>
279-
<TableHead>Actions</TableHead>
283+
{canManageEvents && <TableHead>Actions</TableHead>}
280284
</TableRow>
281285
</TableHeader>
282286
<TableBody>
@@ -308,59 +312,71 @@ export default function CompanyEventsPage() {
308312
</div>
309313
</TableCell>
310314
<TableCell>{event.registered || 0}</TableCell>
311-
<TableCell>
312-
<div className="flex items-center gap-2">
313-
<Link href={`/dashboard/company/${currentCompany.slug}/events/${event.slug}/edit`}>
314-
<Button variant="outline" size="sm">
315-
<Edit className="h-4 w-4" />
316-
</Button>
317-
</Link>
318-
{event.approval_status === 'approved' && (
315+
{canManageEvents ? (
316+
<TableCell>
317+
<div className="flex items-center gap-2">
318+
<Link href={`/dashboard/company/${currentCompany.slug}/events/${event.slug}/edit`}>
319+
<Button variant="outline" size="sm">
320+
<Edit className="h-4 w-4" />
321+
</Button>
322+
</Link>
323+
{event.approval_status === 'approved' && (
324+
<Link href={`/events/${event.slug}`} target="_blank">
325+
<Button variant="outline" size="sm">
326+
<Eye className="h-4 w-4" />
327+
</Button>
328+
</Link>
329+
)}
330+
<AlertDialog>
331+
<AlertDialogTrigger asChild>
332+
<Button
333+
variant="outline"
334+
size="sm"
335+
disabled={deletingEventSlug === event.slug}
336+
>
337+
{deletingEventSlug === event.slug ? (
338+
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
339+
) : (
340+
<Trash2 className="h-4 w-4 text-red-600" />
341+
)}
342+
</Button>
343+
</AlertDialogTrigger>
344+
<AlertDialogContent>
345+
<AlertDialogHeader>
346+
<AlertDialogTitle>Delete Event</AlertDialogTitle>
347+
<AlertDialogDescription>
348+
Are you sure you want to delete &quot;{event.title}&quot;? This action cannot be undone.
349+
{event.registered && event.registered > 0 && (
350+
<span className="block mt-2 text-red-600 font-medium">
351+
Warning: This event has {event.registered} registered participant{event.registered > 1 ? 's' : ''}.
352+
</span>
353+
)}
354+
</AlertDialogDescription>
355+
</AlertDialogHeader>
356+
<AlertDialogFooter>
357+
<AlertDialogCancel>Cancel</AlertDialogCancel>
358+
<AlertDialogAction
359+
onClick={() => handleDeleteEvent(event.slug)}
360+
className="bg-red-600 hover:bg-red-700"
361+
>
362+
Delete
363+
</AlertDialogAction>
364+
</AlertDialogFooter>
365+
</AlertDialogContent>
366+
</AlertDialog>
367+
</div>
368+
</TableCell>
369+
) : (
370+
event.approval_status === 'approved' && (
371+
<TableCell>
319372
<Link href={`/events/${event.slug}`} target="_blank">
320373
<Button variant="outline" size="sm">
321374
<Eye className="h-4 w-4" />
322375
</Button>
323376
</Link>
324-
)}
325-
<AlertDialog>
326-
<AlertDialogTrigger asChild>
327-
<Button
328-
variant="outline"
329-
size="sm"
330-
disabled={deletingEventSlug === event.slug}
331-
>
332-
{deletingEventSlug === event.slug ? (
333-
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
334-
) : (
335-
<Trash2 className="h-4 w-4 text-red-600" />
336-
)}
337-
</Button>
338-
</AlertDialogTrigger>
339-
<AlertDialogContent>
340-
<AlertDialogHeader>
341-
<AlertDialogTitle>Delete Event</AlertDialogTitle>
342-
<AlertDialogDescription>
343-
Are you sure you want to delete &quot;{event.title}&quot;? This action cannot be undone.
344-
{event.registered && event.registered > 0 && (
345-
<span className="block mt-2 text-red-600 font-medium">
346-
Warning: This event has {event.registered} registered participant{event.registered > 1 ? 's' : ''}.
347-
</span>
348-
)}
349-
</AlertDialogDescription>
350-
</AlertDialogHeader>
351-
<AlertDialogFooter>
352-
<AlertDialogCancel>Cancel</AlertDialogCancel>
353-
<AlertDialogAction
354-
onClick={() => handleDeleteEvent(event.slug)}
355-
className="bg-red-600 hover:bg-red-700"
356-
>
357-
Delete
358-
</AlertDialogAction>
359-
</AlertDialogFooter>
360-
</AlertDialogContent>
361-
</AlertDialog>
362-
</div>
363-
</TableCell>
377+
</TableCell>
378+
)
379+
)}
364380
</TableRow>
365381
))}
366382
</TableBody>

app/dashboard/company/[slug]/hackathons/page.tsx

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ interface Hackathon {
2929
}
3030

3131
export default function CompanyHackathonsPage() {
32-
const { currentCompany, loading: companyLoading } = useCompanyContext()
32+
const { currentCompany, userRole, loading: companyLoading } = useCompanyContext()
3333
const isPendingInvitation = usePendingInvitationRedirect()
3434
const [hackathons, setHackathons] = useState<Hackathon[]>([])
3535
const [loading, setLoading] = useState(true)
3636
const [searchTerm, setSearchTerm] = useState('')
3737

38+
const canManageEvents = userRole && ['owner', 'admin', 'editor'].includes(userRole)
39+
3840
const fetchHackathons = useCallback(async () => {
3941
if (!currentCompany) return
4042

@@ -149,15 +151,17 @@ export default function CompanyHackathonsPage() {
149151
<div>
150152
<h1 className="text-3xl font-bold tracking-tight">Hackathons</h1>
151153
<p className="text-muted-foreground mt-1">
152-
Manage your company&apos;s hackathons and coding challenges
154+
{canManageEvents ? "Manage your company's hackathons and coding challenges" : "View your company's hackathons"}
153155
</p>
154156
</div>
155-
<Link href={`/dashboard/company/${currentCompany.slug}/hackathons/create`}>
156-
<Button>
157-
<Plus className="h-4 w-4 mr-2" />
158-
Create Hackathon
159-
</Button>
160-
</Link>
157+
{canManageEvents && (
158+
<Link href={`/dashboard/company/${currentCompany.slug}/hackathons/create`}>
159+
<Button>
160+
<Plus className="h-4 w-4 mr-2" />
161+
Create Hackathon
162+
</Button>
163+
</Link>
164+
)}
161165
</div>
162166

163167
{/* Stats Cards */}
@@ -218,7 +222,7 @@ export default function CompanyHackathonsPage() {
218222
<CardHeader>
219223
<CardTitle>All Hackathons ({filteredHackathons.length})</CardTitle>
220224
<CardDescription>
221-
View and manage all your hackathons
225+
{canManageEvents ? 'View and manage all your hackathons' : 'View all company hackathons'}
222226
</CardDescription>
223227
</CardHeader>
224228
<CardContent>
@@ -247,7 +251,7 @@ export default function CompanyHackathonsPage() {
247251
<TableHead>Approval</TableHead>
248252
<TableHead>Views</TableHead>
249253
<TableHead>Participants</TableHead>
250-
<TableHead>Actions</TableHead>
254+
{canManageEvents && <TableHead>Actions</TableHead>}
251255
</TableRow>
252256
</TableHeader>
253257
<TableBody>
@@ -279,22 +283,34 @@ export default function CompanyHackathonsPage() {
279283
</div>
280284
</TableCell>
281285
<TableCell>{hackathon.registered || 0}</TableCell>
282-
<TableCell>
283-
<div className="flex items-center gap-2">
284-
<Link href={`/dashboard/company/${currentCompany.slug}/hackathons/${hackathon.slug}/edit`}>
285-
<Button variant="outline" size="sm">
286-
<Edit className="h-4 w-4" />
287-
</Button>
288-
</Link>
289-
{hackathon.approval_status === 'approved' && (
286+
{canManageEvents ? (
287+
<TableCell>
288+
<div className="flex items-center gap-2">
289+
<Link href={`/dashboard/company/${currentCompany.slug}/hackathons/${hackathon.slug}/edit`}>
290+
<Button variant="outline" size="sm">
291+
<Edit className="h-4 w-4" />
292+
</Button>
293+
</Link>
294+
{hackathon.approval_status === 'approved' && (
295+
<Link href={`/hackathons/${hackathon.slug}`} target="_blank">
296+
<Button variant="outline" size="sm">
297+
<Eye className="h-4 w-4" />
298+
</Button>
299+
</Link>
300+
)}
301+
</div>
302+
</TableCell>
303+
) : (
304+
hackathon.approval_status === 'approved' && (
305+
<TableCell>
290306
<Link href={`/hackathons/${hackathon.slug}`} target="_blank">
291307
<Button variant="outline" size="sm">
292308
<Eye className="h-4 w-4" />
293309
</Button>
294310
</Link>
295-
)}
296-
</div>
297-
</TableCell>
311+
</TableCell>
312+
)
313+
)}
298314
</TableRow>
299315
))}
300316
</TableBody>

app/dashboard/company/[slug]/page.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ export default function CompanySlugDashboardPage() {
7676
Here&apos;s what&apos;s happening with {currentCompany.name}
7777
</p>
7878
</div>
79-
<div className="flex gap-2">
80-
<Button asChild>
81-
<Link href={`/dashboard/company/${currentCompany.slug}/events/create`}>
82-
<Plus className="mr-2 h-4 w-4" />
83-
Create Event
84-
</Link>
85-
</Button>
86-
</div>
79+
{userRole && ['owner', 'admin', 'editor'].includes(userRole) && (
80+
<div className="flex gap-2">
81+
<Button asChild>
82+
<Link href={`/dashboard/company/${currentCompany.slug}/events/create`}>
83+
<Plus className="mr-2 h-4 w-4" />
84+
Create Event
85+
</Link>
86+
</Button>
87+
</div>
88+
)}
8789
</div>
8890

8991
{/* Subscription Expiry Warning */}
@@ -162,13 +164,15 @@ export default function CompanySlugDashboardPage() {
162164
</p>
163165
</div>
164166
</div>
165-
<div className="pt-4">
166-
<Button asChild variant="outline">
167-
<Link href={`/dashboard/company/${currentCompany.slug}/settings`}>
168-
Edit Company Profile
169-
</Link>
170-
</Button>
171-
</div>
167+
{userRole && ['owner', 'admin'].includes(userRole) && (
168+
<div className="pt-4">
169+
<Button asChild variant="outline">
170+
<Link href={`/dashboard/company/${currentCompany.slug}/settings`}>
171+
Edit Company Profile
172+
</Link>
173+
</Button>
174+
</div>
175+
)}
172176
</CardContent>
173177
</Card>
174178
</div>

app/dashboard/company/[slug]/subscription/page.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { companyService } from '@/lib/services/company-service'
55
import { SubscriptionManagement } from '@/components/subscription/SubscriptionManagement'
66

77
export const metadata: Metadata = {
8-
title: 'Subscription Management | CodeUnia',
9-
description: 'Manage your company subscription and billing',
8+
title: 'Subscription | CodeUnia',
9+
description: 'View your company subscription and billing information',
1010
}
1111

1212
interface PageProps {
@@ -59,17 +59,19 @@ export default async function SubscriptionPage({ params }: PageProps) {
5959
redirect('/dashboard/company')
6060
}
6161

62-
// Only owners and admins can manage subscription
63-
if (!['owner', 'admin'].includes(membership.role)) {
64-
redirect(`/dashboard/company/${slug}`)
65-
}
62+
// Check if user can manage subscription
63+
const canManageSubscription = ['owner', 'admin'].includes(membership.role)
6664

6765
return (
6866
<div className="container mx-auto py-8 px-4 max-w-6xl">
6967
<div className="mb-8">
70-
<h1 className="text-3xl font-bold mb-2">Subscription Management</h1>
68+
<h1 className="text-3xl font-bold mb-2">
69+
{canManageSubscription ? 'Subscription Management' : 'Subscription Information'}
70+
</h1>
7171
<p className="text-muted-foreground">
72-
Manage your subscription plan and view usage details
72+
{canManageSubscription
73+
? 'Manage your subscription plan and view usage details'
74+
: 'View your company subscription plan and usage details'}
7375
</p>
7476
</div>
7577

0 commit comments

Comments
 (0)