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
2 changes: 1 addition & 1 deletion app/admin/events/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export default function AdminEvents() {
</TableCell>
<TableCell>
<Badge variant="outline">
{event.status}
{event.status.charAt(0).toUpperCase() + event.status.slice(1)}
</Badge>
</TableCell>
<TableCell>
Expand Down
3 changes: 3 additions & 0 deletions app/api/admin/events/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ export async function GET(request: NextRequest) {

const serviceSupabase = createServiceClient(supabaseUrl, supabaseServiceKey)

// Only show events that need moderation (pending, approved, rejected)
// Exclude drafts as they haven't been submitted for review yet
const { data: events, error } = await serviceSupabase
.from('events')
.select('*')
.in('approval_status', ['pending', 'approved', 'rejected'])
.order('created_at', { ascending: false })
.range(offset, offset + limit - 1)

Expand Down
28 changes: 23 additions & 5 deletions app/dashboard/company/[slug]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ import {
} from 'lucide-react'
import { useToast } from '@/components/ui/use-toast'
import { SUBSCRIPTION_LIMITS } from '@/types/company'
import { useSubscription } from '@/hooks/useSubscription'

export default function CompanySettingsPage() {
const { currentCompany, userRole, loading: contextLoading, refreshCompany } = useCompanyContext()
const { usage } = useSubscription(currentCompany?.slug)
const { toast } = useToast()
const [loading, setLoading] = useState(false)
const [uploadingLogo, setUploadingLogo] = useState(false)
Expand Down Expand Up @@ -793,29 +795,45 @@ export default function CompanySettingsPage() {
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm text-zinc-400">Events Created</span>
<span className="text-sm font-medium text-white">
{currentCompany.total_events}
<span className="text-sm text-zinc-400">Events Created (This Month)</span>
<span className={`text-sm font-medium ${
usage && subscriptionLimits.events_per_month !== null &&
usage.events_this_month > subscriptionLimits.events_per_month
? 'text-red-400'
: 'text-white'
}`}>
{usage?.events_this_month ?? 0}
{subscriptionLimits.events_per_month !== null &&
` / ${subscriptionLimits.events_per_month}`}
</span>
</div>
<div className="w-full bg-zinc-800 rounded-full h-2">
<div
className="bg-gradient-to-r from-primary to-purple-600 h-2 rounded-full"
className={`h-2 rounded-full ${
usage && subscriptionLimits.events_per_month !== null &&
usage.events_this_month > subscriptionLimits.events_per_month
? 'bg-gradient-to-r from-red-500 to-red-600'
: 'bg-gradient-to-r from-primary to-purple-600'
}`}
style={{
width:
subscriptionLimits.events_per_month === null
? '0%'
: `${Math.min(
(currentCompany.total_events /
((usage?.events_this_month ?? 0) /
subscriptionLimits.events_per_month) *
100,
100
)}%`,
}}
/>
</div>
{usage && subscriptionLimits.events_per_month !== null &&
usage.events_this_month > subscriptionLimits.events_per_month && (
<p className="text-xs text-red-400 mt-1">
⚠️ You have exceeded your monthly limit. Please upgrade your plan.
</p>
)}
</div>
</CardContent>
</Card>
Expand Down
198 changes: 198 additions & 0 deletions app/protected/notifications/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
'use client'

import { useState } from 'react'
import { useNotifications } from '@/hooks/useNotifications'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Bell, Check, CheckCheck, Trash2, ExternalLink } from 'lucide-react'
import { formatDistanceToNow } from 'date-fns'
import Link from 'next/link'
import { cn } from '@/lib/utils'

export default function NotificationsPage() {
const { notifications, loading, unreadCount, markAsRead, markAllAsRead, deleteNotification } = useNotifications()
const [filter, setFilter] = useState<'all' | 'unread'>('all')

const filteredNotifications = filter === 'unread'
? notifications.filter(n => !n.read)
: notifications

const getNotificationIcon = () => {
return <Bell className="h-5 w-5" />
}

const getNotificationColor = (type: string) => {
switch (type) {
case 'event_approved':
return 'text-green-500'
case 'event_rejected':
return 'text-red-500'
case 'event_changes_requested':
return 'text-orange-500'
default:
return 'text-blue-500'
}
}

if (loading) {
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>
)
}

return (
<div className="container max-w-4xl mx-auto py-8 px-4">
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">Notifications</h1>
<p className="text-muted-foreground mt-1">
Stay updated with your events and activities
</p>
</div>
{unreadCount > 0 && (
<Button onClick={markAllAsRead} variant="outline" size="sm">
<CheckCheck className="h-4 w-4 mr-2" />
Mark all as read
</Button>
)}
</div>

{/* Stats */}
<div className="grid grid-cols-2 gap-4">
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium">Total</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{notifications.length}</div>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium">Unread</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-primary">{unreadCount}</div>
</CardContent>
</Card>
</div>

{/* Tabs */}
<Tabs value={filter} onValueChange={(v) => setFilter(v as 'all' | 'unread')}>
<TabsList className="grid w-full max-w-md grid-cols-2">
<TabsTrigger value="all">
All ({notifications.length})
</TabsTrigger>
<TabsTrigger value="unread">
Unread ({unreadCount})
</TabsTrigger>
</TabsList>

<TabsContent value={filter} className="mt-6 space-y-4">
{filteredNotifications.length === 0 ? (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
<Bell className="h-12 w-12 text-muted-foreground mb-4" />
<p className="text-muted-foreground text-center">
{filter === 'unread'
? "You're all caught up! No unread notifications."
: "No notifications yet."}
</p>
</CardContent>
</Card>
) : (
filteredNotifications.map((notification) => (
<Card
key={notification.id}
className={cn(
"transition-all hover:shadow-md",
!notification.read && "border-l-4 border-l-primary bg-primary/5"
)}
>
<CardContent className="p-4">
<div className="flex items-start gap-4">
{/* Icon */}
<div className={cn(
"p-2 rounded-full bg-muted flex-shrink-0",
getNotificationColor(notification.type)
)}>
{getNotificationIcon()}
</div>

{/* Content */}
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2 mb-1">
<h3 className="font-semibold text-sm">
{notification.title}
</h3>
{!notification.read && (
<Badge variant="default" className="flex-shrink-0">
New
</Badge>
)}
</div>
<p className="text-sm text-muted-foreground mb-2">
{notification.message}
</p>
<div className="flex items-center gap-4 text-xs text-muted-foreground">
<span>
{formatDistanceToNow(new Date(notification.created_at), { addSuffix: true })}
</span>
{notification.company_id && (
<span className="flex items-center gap-1">
<span className="w-1 h-1 rounded-full bg-muted-foreground" />
Company notification
</span>
)}
</div>
</div>

{/* Actions */}
<div className="flex items-center gap-2 flex-shrink-0">
{notification.action_url && (
<Button
variant="ghost"
size="sm"
asChild
>
<Link href={notification.action_url}>
<ExternalLink className="h-4 w-4" />
</Link>
</Button>
)}
{!notification.read && (
<Button
variant="ghost"
size="sm"
onClick={() => markAsRead(notification.id)}
title="Mark as read"
>
<Check className="h-4 w-4" />
</Button>
)}
<Button
variant="ghost"
size="sm"
onClick={() => deleteNotification(notification.id)}
title="Delete"
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
</CardContent>
</Card>
))
)}
</TabsContent>
</Tabs>
</div>
</div>
)
}
57 changes: 0 additions & 57 deletions check-database-status.sql

This file was deleted.

55 changes: 0 additions & 55 deletions check-schema-permissions.sql

This file was deleted.

Loading
Loading