diff --git a/app/admin/events/page.tsx b/app/admin/events/page.tsx index 5b911f894..67ba2064b 100644 --- a/app/admin/events/page.tsx +++ b/app/admin/events/page.tsx @@ -360,7 +360,7 @@ export default function AdminEvents() { - {event.status} + {event.status.charAt(0).toUpperCase() + event.status.slice(1)} diff --git a/app/api/admin/events/route.ts b/app/api/admin/events/route.ts index 3ee136b4a..041be43be 100644 --- a/app/api/admin/events/route.ts +++ b/app/api/admin/events/route.ts @@ -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) diff --git a/app/dashboard/company/[slug]/settings/page.tsx b/app/dashboard/company/[slug]/settings/page.tsx index e2f3c022d..b8c0abd74 100644 --- a/app/dashboard/company/[slug]/settings/page.tsx +++ b/app/dashboard/company/[slug]/settings/page.tsx @@ -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) @@ -793,22 +795,32 @@ export default function CompanySettingsPage() {
- Events Created - - {currentCompany.total_events} + Events Created (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}`}
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 @@ -816,6 +828,12 @@ export default function CompanySettingsPage() { }} />
+ {usage && subscriptionLimits.events_per_month !== null && + usage.events_this_month > subscriptionLimits.events_per_month && ( +

+ ⚠️ You have exceeded your monthly limit. Please upgrade your plan. +

+ )}
diff --git a/app/protected/notifications/page.tsx b/app/protected/notifications/page.tsx new file mode 100644 index 000000000..667b0c293 --- /dev/null +++ b/app/protected/notifications/page.tsx @@ -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 + } + + 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 ( +
+
+
+ ) + } + + return ( +
+
+ {/* Header */} +
+
+

Notifications

+

+ Stay updated with your events and activities +

+
+ {unreadCount > 0 && ( + + )} +
+ + {/* Stats */} +
+ + + Total + + +
{notifications.length}
+
+
+ + + Unread + + +
{unreadCount}
+
+
+
+ + {/* Tabs */} + setFilter(v as 'all' | 'unread')}> + + + All ({notifications.length}) + + + Unread ({unreadCount}) + + + + + {filteredNotifications.length === 0 ? ( + + + +

+ {filter === 'unread' + ? "You're all caught up! No unread notifications." + : "No notifications yet."} +

+
+
+ ) : ( + filteredNotifications.map((notification) => ( + + +
+ {/* Icon */} +
+ {getNotificationIcon()} +
+ + {/* Content */} +
+
+

+ {notification.title} +

+ {!notification.read && ( + + New + + )} +
+

+ {notification.message} +

+
+ + {formatDistanceToNow(new Date(notification.created_at), { addSuffix: true })} + + {notification.company_id && ( + + + Company notification + + )} +
+
+ + {/* Actions */} +
+ {notification.action_url && ( + + )} + {!notification.read && ( + + )} + +
+
+
+
+ )) + )} +
+
+
+
+ ) +} diff --git a/check-database-status.sql b/check-database-status.sql deleted file mode 100644 index be637ca33..000000000 --- a/check-database-status.sql +++ /dev/null @@ -1,57 +0,0 @@ --- ===================================================== --- DATABASE STATUS CHECK --- ===================================================== --- Copy and paste this entire query into your Supabase SQL Editor --- and share the results with me - --- 1. Check if profiles table exists -SELECT - 'Table Check' as check_type, - table_name, - CASE - WHEN table_name = 'profiles' THEN 'EXISTS' - ELSE 'OTHER TABLE' - END as status -FROM information_schema.tables -WHERE table_schema = 'public' -AND table_name = 'profiles'; - --- 2. Check if update_username function exists -SELECT - 'Function Check' as check_type, - proname as function_name, - CASE - WHEN proname = 'update_username' THEN 'EXISTS' - ELSE 'OTHER FUNCTION' - END as status -FROM pg_proc -WHERE proname = 'update_username'; - --- 3. Check if set_username function exists -SELECT - 'Function Check' as check_type, - proname as function_name, - CASE - WHEN proname = 'set_username' THEN 'EXISTS' - ELSE 'OTHER FUNCTION' - END as status -FROM pg_proc -WHERE proname = 'set_username'; - --- 4. List all tables in public schema -SELECT - 'All Tables' as check_type, - table_name, - 'EXISTS' as status -FROM information_schema.tables -WHERE table_schema = 'public' -ORDER BY table_name; - --- 5. List all functions in public schema -SELECT - 'All Functions' as check_type, - proname as function_name, - 'EXISTS' as status -FROM pg_proc -WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public') -ORDER BY proname; diff --git a/check-schema-permissions.sql b/check-schema-permissions.sql deleted file mode 100644 index 92b340d82..000000000 --- a/check-schema-permissions.sql +++ /dev/null @@ -1,55 +0,0 @@ --- ===================================================== --- CHECK SCHEMA AND PERMISSIONS --- ===================================================== --- Copy and paste this query to check schema and permissions - --- 1. Check current schema -SELECT - 'Current Schema' as check_type, - current_schema() as schema_name, - 'Current schema being used' as info; - --- 2. Check if profiles table exists in different schemas -SELECT - 'Table Schema Check' as check_type, - table_schema, - table_name, - 'EXISTS' as status -FROM information_schema.tables -WHERE table_name = 'profiles' -ORDER BY table_schema; - --- 3. Check table permissions for current user -SELECT - 'Table Permissions' as check_type, - table_name, - privilege_type, - grantee -FROM information_schema.table_privileges -WHERE table_name = 'profiles' -AND table_schema = 'public'; - --- 4. Check function permissions -SELECT - 'Function Permissions' as check_type, - routine_name, - privilege_type, - grantee -FROM information_schema.routine_privileges -WHERE routine_name = 'update_username' -AND routine_schema = 'public'; - --- 5. Check if we can access profiles table directly -SELECT - 'Direct Access Test' as check_type, - COUNT(*) as row_count, - 'Can access profiles table' as info -FROM public.profiles; - --- 6. Check search_path -SELECT - 'Search Path' as check_type, - setting as search_path, - 'Current search path' as info -FROM pg_settings -WHERE name = 'search_path'; diff --git a/components/dashboard/CompanyDashboard.tsx b/components/dashboard/CompanyDashboard.tsx index 0c55e630b..76f167aa1 100644 --- a/components/dashboard/CompanyDashboard.tsx +++ b/components/dashboard/CompanyDashboard.tsx @@ -326,7 +326,6 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) { ))}
@@ -449,19 +448,15 @@ function ActivityItem({ activity }: ActivityItemProps) { // Upcoming Event Item Component interface UpcomingEventItemProps { event: UpcomingEvent - companySlug: string } -function UpcomingEventItem({ event, companySlug }: UpcomingEventItemProps) { +function UpcomingEventItem({ event }: UpcomingEventItemProps) { const registrationPercentage = event.capacity ? (event.registrations / event.capacity) * 100 : 0 return ( - +

{event.title}

@@ -484,7 +479,7 @@ function UpcomingEventItem({ event, companySlug }: UpcomingEventItemProps) {
)} - +
) } diff --git a/components/notifications/NotificationCenter.tsx b/components/notifications/NotificationCenter.tsx index 34124cb46..fb266703d 100644 --- a/components/notifications/NotificationCenter.tsx +++ b/components/notifications/NotificationCenter.tsx @@ -13,6 +13,7 @@ import { useNotifications } from '@/hooks/useNotifications' import { NotificationItem } from './NotificationItem' import { Separator } from '@/components/ui/separator' import { useState } from 'react' +import Link from 'next/link' export function NotificationCenter() { const { @@ -137,13 +138,11 @@ export function NotificationCenter() { variant="ghost" size="sm" className="w-full text-xs" - onClick={() => { - setOpen(false) - // Navigate to notifications page if you have one - // router.push('/notifications') - }} + asChild > - View all notifications + setOpen(false)}> + View all notifications + diff --git a/lib/services/events.ts b/lib/services/events.ts index 1929f0700..a9d017500 100644 --- a/lib/services/events.ts +++ b/lib/services/events.ts @@ -357,7 +357,8 @@ class EventsService { // if price is not "Free", payment must be "Required" const paymentValue = eventData.price === 'Free' ? 'Not Required' : (eventData.payment || 'Required') - // Remove status field - events start as draft/pending based on approval_status + // Remove status field - events start as draft by default + // They can be submitted for approval later via submitForApproval // eslint-disable-next-line @typescript-eslint/no-unused-vars const { status, ...eventDataWithoutStatus } = eventData @@ -365,7 +366,7 @@ class EventsService { ...eventDataWithoutStatus, company_id: companyId, created_by: userId, - approval_status: 'pending' as const, + approval_status: 'draft' as const, is_codeunia_event: false, payment: paymentValue, } diff --git a/test-fixed-function.sql b/test-fixed-function.sql deleted file mode 100644 index d25275a8d..000000000 --- a/test-fixed-function.sql +++ /dev/null @@ -1,26 +0,0 @@ --- ===================================================== --- TEST THE FIXED FUNCTION --- ===================================================== --- Copy and paste this query to test the fixed function - --- 1. Test the function with a dummy call (this should work now) -SELECT - 'Function Test' as test_type, - update_username(auth.uid(), 'test_username_456') as result, - 'Should return true or false, not error' as expected; - --- 2. Check if the function definition is correct now -SELECT - 'Function Definition' as test_type, - proname as function_name, - pg_get_function_identity_arguments(oid) as arguments, - 'Function should reference public.profiles' as expected -FROM pg_proc -WHERE proname = 'update_username'; - --- 3. Verify we can still access the profiles table -SELECT - 'Table Access' as test_type, - COUNT(*) as row_count, - 'Should show number of profiles' as expected -FROM public.profiles; diff --git a/test-username-function.sql b/test-username-function.sql deleted file mode 100644 index e708ef8c4..000000000 --- a/test-username-function.sql +++ /dev/null @@ -1,37 +0,0 @@ --- ===================================================== --- TEST USERNAME FUNCTION --- ===================================================== --- Copy and paste this query to test the update_username function - --- 1. First, let's see the current user and their profile -SELECT - 'Current User' as test_type, - auth.uid() as user_id, - 'User ID from auth' as info; - --- 2. Check if current user has a profile -SELECT - 'Profile Check' as test_type, - id, - username, - username_editable, - username_set, - first_name, - last_name -FROM profiles -WHERE id = auth.uid(); - --- 3. Test the update_username function with a dummy call --- (This will show us the exact error if any) -SELECT - 'Function Test' as test_type, - update_username(auth.uid(), 'test_username_123') as result; - --- 4. Check function definition -SELECT - 'Function Definition' as test_type, - proname as function_name, - pg_get_function_identity_arguments(oid) as arguments, - prosrc as function_source -FROM pg_proc -WHERE proname = 'update_username';