Skip to content

Commit 0cb9e6a

Browse files
fix(web): Adjust invite page height and padding
Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent 09f6737 commit 0cb9e6a

File tree

2 files changed

+136
-45
lines changed

2 files changed

+136
-45
lines changed

web/src/app/invites/[token]/page.tsx

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import { useEffect, useState } from 'react'
44
import { useRouter } from 'next/navigation'
55
import { useSession } from 'next-auth/react'
66
import { Button } from '@/components/ui/button'
7-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
7+
import {
8+
Card,
9+
CardContent,
10+
CardDescription,
11+
CardHeader,
12+
CardTitle,
13+
} from '@/components/ui/card'
814
import { Skeleton } from '@/components/ui/skeleton'
915
import { CheckCircle, XCircle, Clock, Users } from 'lucide-react'
1016

@@ -54,7 +60,9 @@ export default function InvitationPage({ params }: PageProps) {
5460

5561
const acceptInvitation = async () => {
5662
if (!session) {
57-
router.push(`/login?callbackUrl=${encodeURIComponent(window.location.href)}`)
63+
router.push(
64+
`/login?callbackUrl=${encodeURIComponent(window.location.href)}`
65+
)
5866
return
5967
}
6068

@@ -85,7 +93,7 @@ export default function InvitationPage({ params }: PageProps) {
8593

8694
if (loading) {
8795
return (
88-
<div className="min-h-screen flex items-center justify-center bg-black text-white">
96+
<div className="flex items-center justify-center bg-black text-white">
8997
<Card className="w-full max-w-md bg-gray-900 border border-gray-700 shadow-xl">
9098
<CardHeader>
9199
<Skeleton className="h-6 w-3/4 bg-gray-700" />
@@ -101,7 +109,7 @@ export default function InvitationPage({ params }: PageProps) {
101109

102110
if (error) {
103111
return (
104-
<div className="min-h-screen flex items-center justify-center bg-black text-white">
112+
<div className="flex items-center justify-center bg-black text-white">
105113
<Card className="w-full max-w-md bg-gray-900 border border-red-500 shadow-xl">
106114
<CardHeader>
107115
<div className="flex items-center gap-2">
@@ -111,9 +119,9 @@ export default function InvitationPage({ params }: PageProps) {
111119
<CardDescription className="text-gray-400">{error}</CardDescription>
112120
</CardHeader>
113121
<CardContent>
114-
<Button
115-
onClick={() => router.push('/')}
116-
variant="outline"
122+
<Button
123+
onClick={() => router.push('/')}
124+
variant="outline"
117125
className="w-full border-gray-600 hover:bg-gray-700 text-white"
118126
>
119127
Go to Homepage
@@ -126,12 +134,14 @@ export default function InvitationPage({ params }: PageProps) {
126134

127135
if (success) {
128136
return (
129-
<div className="min-h-screen flex items-center justify-center bg-black text-white">
137+
<div className="flex items-center justify-center bg-black text-white">
130138
<Card className="w-full max-w-md bg-gray-900 border border-green-500 shadow-xl">
131139
<CardHeader>
132140
<div className="flex items-center gap-2">
133141
<CheckCircle className="h-5 w-5 text-green-400" />
134-
<CardTitle className="text-green-400">Welcome to {invitation?.organization_name}!</CardTitle>
142+
<CardTitle className="text-green-400">
143+
Welcome to {invitation?.organization_name}!
144+
</CardTitle>
135145
</div>
136146
<CardDescription className="text-gray-400">
137147
You've successfully joined the organization. Redirecting...
@@ -149,15 +159,16 @@ export default function InvitationPage({ params }: PageProps) {
149159
const isExpired = new Date(invitation.expires_at) < new Date()
150160

151161
return (
152-
<div className="min-h-screen flex items-center justify-center bg-black text-white relative overflow-hidden">
162+
<div className="flex items-center justify-center bg-black text-white relative overflow-hidden py-12">
153163
<Card className="w-full max-w-md bg-gray-900 border border-gray-700 shadow-xl z-10">
154164
<CardHeader>
155165
<div className="flex items-center gap-2">
156166
<Users className="h-5 w-5 text-blue-400" />
157167
<CardTitle>Organization Invitation</CardTitle>
158168
</div>
159169
<CardDescription className="text-gray-400">
160-
{invitation.inviter_name} has invited you to join {invitation.organization_name}
170+
{invitation.inviter_name} has invited you to join{' '}
171+
{invitation.organization_name}
161172
</CardDescription>
162173
</CardHeader>
163174
<CardContent className="space-y-4">
@@ -182,9 +193,9 @@ export default function InvitationPage({ params }: PageProps) {
182193
{isExpired ? (
183194
<div className="text-center">
184195
<p className="text-red-400 mb-4">This invitation has expired.</p>
185-
<Button
186-
onClick={() => router.push('/')}
187-
variant="outline"
196+
<Button
197+
onClick={() => router.push('/')}
198+
variant="outline"
188199
className="w-full border-gray-600 hover:bg-gray-700 text-white"
189200
>
190201
Go to Homepage
@@ -199,28 +210,33 @@ export default function InvitationPage({ params }: PageProps) {
199210
<p className="text-gray-400 mb-4">
200211
Please sign in to accept this invitation.
201212
</p>
202-
<Button
203-
onClick={() => router.push(`/login?callbackUrl=${encodeURIComponent(window.location.href)}`)}
213+
<Button
214+
onClick={() =>
215+
router.push(
216+
`/login?callbackUrl=${encodeURIComponent(window.location.href)}`
217+
)
218+
}
204219
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
205220
>
206221
Sign In to Accept
207222
</Button>
208223
</div>
209224
) : session.user?.email !== invitation.email ? (
210-
<div className="text-center">
225+
<div className="text-left">
211226
<p className="text-red-400 mb-4">
212-
This invitation is for {invitation.email}, but you're signed in as {session.user?.email}.
227+
This invitation is for {invitation.email}, but you're signed
228+
in as {session.user?.email}.
213229
</p>
214-
<Button
230+
<Button
215231
onClick={() => router.push('/login')}
216-
variant="outline"
232+
variant="outline"
217233
className="w-full border-gray-600 hover:bg-gray-700 text-white"
218234
>
219235
Sign in with correct account
220236
</Button>
221237
</div>
222238
) : (
223-
<Button
239+
<Button
224240
onClick={acceptInvitation}
225241
disabled={accepting}
226242
className="w-full bg-green-600 hover:bg-green-700 text-white"
@@ -234,4 +250,4 @@ export default function InvitationPage({ params }: PageProps) {
234250
</Card>
235251
</div>
236252
)
237-
}
253+
}

web/src/components/organization/team-management.tsx

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,12 @@ export function TeamManagement({
9797
new Set()
9898
)
9999
const [refreshing, setRefreshing] = useState(false)
100-
const [confirmResendDialogOpen, setConfirmResendDialogOpen] = useState(false);
101-
const [currentInvitationToResend, setCurrentInvitationToResend] = useState<Invitation | null>(null);
100+
const [confirmResendDialogOpen, setConfirmResendDialogOpen] = useState(false)
101+
const [currentInvitationToResend, setCurrentInvitationToResend] =
102+
useState<Invitation | null>(null)
103+
const [confirmCancelDialogOpen, setConfirmCancelDialogOpen] = useState(false)
104+
const [currentInvitationToCancel, setCurrentInvitationToCancel] =
105+
useState<Invitation | null>(null)
102106

103107
const canManageTeam = userRole === 'owner' || userRole === 'admin'
104108
const isMobile = useIsMobile()
@@ -157,10 +161,6 @@ export function TeamManagement({
157161
}
158162
}
159163

160-
const handleRefresh = () => {
161-
fetchTeamData(false)
162-
}
163-
164164
const handleInviteMember = async () => {
165165
if (!inviteForm.email.trim()) {
166166
toast({
@@ -346,16 +346,16 @@ export function TeamManagement({
346346
}
347347

348348
const handleInitiateResend = (invitation: Invitation) => {
349-
setCurrentInvitationToResend(invitation);
350-
setConfirmResendDialogOpen(true);
351-
};
349+
setCurrentInvitationToResend(invitation)
350+
setConfirmResendDialogOpen(true)
351+
}
352352

353353
const handleConfirmResend = async () => {
354-
if (!currentInvitationToResend) return;
354+
if (!currentInvitationToResend) return
355355

356-
const email = currentInvitationToResend.email;
357-
setResendingInvites((prev) => new Set(prev).add(email));
358-
setConfirmResendDialogOpen(false); // Close dialog
356+
const email = currentInvitationToResend.email
357+
setResendingInvites((prev) => new Set(prev).add(email))
358+
setConfirmResendDialogOpen(false) // Close dialog
359359

360360
try {
361361
const response = await fetch(
@@ -392,18 +392,48 @@ export function TeamManagement({
392392
newSet.delete(email)
393393
return newSet
394394
})
395-
setCurrentInvitationToResend(null);
395+
setCurrentInvitationToResend(null)
396396
}
397397
}
398398

399399
const handleCancelInvitation = async (email: string) => {
400-
if (
401-
!confirm(
402-
`Are you sure you want to remove ${email} from the organization?`
400+
try {
401+
const response = await fetch(
402+
`/api/orgs/${organizationId}/invitations/${encodeURIComponent(email)}`,
403+
{
404+
method: 'DELETE',
405+
}
403406
)
404-
) {
405-
return
407+
408+
if (!response.ok) {
409+
const data = await response.json()
410+
throw new Error(data.error || 'Failed to cancel invitation')
411+
}
412+
413+
toast({
414+
title: 'Success',
415+
description: 'Invitation cancelled',
416+
})
417+
418+
fetchTeamData(false) // Refresh without showing skeleton
419+
} catch (error) {
420+
toast({
421+
title: 'Error',
422+
description:
423+
error instanceof Error
424+
? error.message
425+
: 'Failed to cancel invitation',
426+
variant: 'destructive',
427+
})
406428
}
429+
}
430+
431+
const handleConfirmCancel = async () => {
432+
if (!currentInvitationToCancel) return
433+
434+
const email = currentInvitationToCancel.email
435+
// We can add a loading state for cancelling if needed, similar to resending
436+
setConfirmCancelDialogOpen(false) // Close dialog
407437

408438
try {
409439
const response = await fetch(
@@ -433,6 +463,8 @@ export function TeamManagement({
433463
: 'Failed to cancel invitation',
434464
variant: 'destructive',
435465
})
466+
} finally {
467+
setCurrentInvitationToCancel(null)
436468
}
437469
}
438470

@@ -969,7 +1001,10 @@ export function TeamManagement({
9691001
)}
9701002

9711003
{/* Confirmation Dialog for Resend */}
972-
<Dialog open={confirmResendDialogOpen} onOpenChange={setConfirmResendDialogOpen}>
1004+
<Dialog
1005+
open={confirmResendDialogOpen}
1006+
onOpenChange={setConfirmResendDialogOpen}
1007+
>
9731008
<DialogContent>
9741009
<DialogHeader>
9751010
<DialogTitle>Confirm Resend Invitation</DialogTitle>
@@ -980,12 +1015,52 @@ export function TeamManagement({
9801015
</DialogHeader>
9811016
<DialogFooter className="gap-2 sm:gap-0">
9821017
<DialogClose asChild>
983-
<Button variant="destructive" onClick={() => setCurrentInvitationToResend(null)}>
1018+
<Button
1019+
variant="destructive"
1020+
onClick={() => setCurrentInvitationToResend(null)}
1021+
>
9841022
Cancel
9851023
</Button>
9861024
</DialogClose>
987-
<Button onClick={handleConfirmResend} disabled={resendingInvites.has(currentInvitationToResend?.email || '')}>
988-
{resendingInvites.has(currentInvitationToResend?.email || '') ? 'Resending...' : 'Confirm Resend'}
1025+
<Button
1026+
onClick={handleConfirmResend}
1027+
disabled={resendingInvites.has(
1028+
currentInvitationToResend?.email || ''
1029+
)}
1030+
>
1031+
{resendingInvites.has(currentInvitationToResend?.email || '')
1032+
? 'Resending...'
1033+
: 'Confirm Resend'}
1034+
</Button>
1035+
</DialogFooter>
1036+
</DialogContent>
1037+
</Dialog>
1038+
1039+
{/* Confirmation Dialog for Cancel Invitation */}
1040+
<Dialog
1041+
open={confirmCancelDialogOpen}
1042+
onOpenChange={setConfirmCancelDialogOpen}
1043+
>
1044+
<DialogContent>
1045+
<DialogHeader>
1046+
<DialogTitle>Confirm Cancel Invitation</DialogTitle>
1047+
<DialogDescription>
1048+
Are you sure you want to cancel the invitation for{' '}
1049+
<strong>{currentInvitationToCancel?.email}</strong>? This action
1050+
cannot be undone.
1051+
</DialogDescription>
1052+
</DialogHeader>
1053+
<DialogFooter className="gap-2 sm:gap-0">
1054+
<DialogClose asChild>
1055+
<Button
1056+
variant="outline"
1057+
onClick={() => setCurrentInvitationToCancel(null)}
1058+
>
1059+
Back
1060+
</Button>
1061+
</DialogClose>
1062+
<Button variant="destructive" onClick={handleConfirmCancel}>
1063+
Confirm Cancel
9891064
</Button>
9901065
</DialogFooter>
9911066
</DialogContent>

0 commit comments

Comments
 (0)