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
66 changes: 66 additions & 0 deletions app/admin/support/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ import {
import { toast } from 'sonner'
import Link from 'next/link'

interface TicketReply {
id: string
ticket_id: string
admin_id: string
message: string
created_at: string
updated_at: string
admin?: {
id: string
email: string
first_name?: string
last_name?: string
avatar_url?: string
}
}

interface SupportTicket {
id: string
user_id: string
Expand All @@ -37,6 +53,7 @@ interface SupportTicket {
last_name?: string
avatar_url?: string
}
replies?: TicketReply[]
}

export default function TicketDetailPage() {
Expand Down Expand Up @@ -235,6 +252,55 @@ export default function TicketDetailPage() {
</CardContent>
</Card>

{/* Reply History */}
{ticket.replies && ticket.replies.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<MessageSquare className="h-5 w-5 text-purple-500" />
Reply History ({ticket.replies.length})
</CardTitle>
<CardDescription>
Previous responses sent to the user
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{ticket.replies.map((reply, index) => (
<div
key={reply.id}
className="border-l-4 border-purple-500/30 bg-purple-500/5 rounded-r-lg p-4 space-y-2"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="h-8 w-8 rounded-full bg-gradient-to-br from-purple-500 to-blue-600 flex items-center justify-center text-white text-sm font-semibold">
{reply.admin?.first_name?.[0] || reply.admin?.email[0].toUpperCase() || 'A'}
</div>
<div>
<p className="text-sm font-medium">
{reply.admin?.first_name && reply.admin?.last_name
? `${reply.admin.first_name} ${reply.admin.last_name}`
: reply.admin?.email || 'Admin'}
</p>
<p className="text-xs text-muted-foreground">
{new Date(reply.created_at).toLocaleString()}
</p>
</div>
</div>
<Badge variant="outline" className="text-xs">
Reply #{index + 1}
</Badge>
</div>
<div className="pl-10">
<p className="text-sm text-foreground whitespace-pre-wrap">
{reply.message}
</p>
</div>
</div>
))}
</CardContent>
</Card>
)}

{/* Reply to User */}
<Card className="border-blue-500/20 bg-blue-500/5">
<CardHeader>
Expand Down
22 changes: 19 additions & 3 deletions app/api/admin/support/tickets/[id]/reply/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,28 @@ export async function POST(

console.log('✅ Reply email sent successfully')

// TODO: Save reply to database (for reply history)
// This would go in a support_ticket_replies table
// Save reply to database for history
const { data: reply, error: replyError } = await supabase
.from('support_ticket_replies')
.insert({
ticket_id: ticket.id,
admin_id: user.id,
message: message.trim()
})
.select()
.single()

if (replyError) {
console.error('❌ Failed to save reply to database:', replyError)
// Don't fail the request since email was sent successfully
} else {
console.log('✅ Reply saved to database:', reply.id)
}

return NextResponse.json({
success: true,
message: 'Reply sent successfully'
message: 'Reply sent successfully',
reply
})
} catch (error) {
console.error('Error in reply API:', error)
Expand Down
25 changes: 23 additions & 2 deletions app/api/admin/support/tickets/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,31 @@ export async function GET(
.eq('id', ticket.user_id)
.single()

// Combine ticket with user data
// Get reply history
const { data: replies } = await supabase
.from('support_ticket_replies')
.select('*')
.eq('ticket_id', id)
.order('created_at', { ascending: true })

// Get admin profiles for replies
const adminIds = [...new Set(replies?.map(r => r.admin_id) || [])]
const { data: adminProfiles } = await supabase
.from('profiles')
.select('id, email, first_name, last_name, avatar_url')
.in('id', adminIds)

// Map admin data to replies
const repliesWithAdmins = replies?.map(reply => ({
...reply,
admin: adminProfiles?.find(p => p.id === reply.admin_id) || null
})) || []

// Combine ticket with user data and replies
const ticketWithUser = {
...ticket,
user: userProfile || null
user: userProfile || null,
replies: repliesWithAdmins
}

return NextResponse.json({ ticket: ticketWithUser })
Expand Down
Loading