fix: correct fee settings button link and improve calendar theming
- Fix fee settings button in dashboard to link to /settings/fees instead of /calendar - Implement proper theme management system for calendar page - Add theme background handler and data-theme-background attribute - Replace broken theme import with complete theme management - Both dashboard and calendar now properly support light/dark themes - Fixed glassmorphism CSS variables and theme switching 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
88
scripts/clear-cache.sh
Executable file
88
scripts/clear-cache.sh
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Clear Cache Script for BCT Development
|
||||||
|
# This script clears all types of caches that might interfere with development
|
||||||
|
|
||||||
|
echo "🧹 Starting comprehensive cache clearing..."
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_step() {
|
||||||
|
echo -e "${BLUE}$1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}✅ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}❌ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Clear Node.js module cache
|
||||||
|
print_step "1. Clearing Node.js module cache..."
|
||||||
|
rm -rf node_modules/.cache 2>/dev/null || true
|
||||||
|
print_success "Node.js cache cleared"
|
||||||
|
|
||||||
|
# 2. Clear npm cache
|
||||||
|
print_step "2. Clearing npm cache..."
|
||||||
|
npm cache clean --force 2>/dev/null || true
|
||||||
|
print_success "npm cache cleared"
|
||||||
|
|
||||||
|
# 3. Clear Astro build cache
|
||||||
|
print_step "3. Clearing Astro build cache..."
|
||||||
|
rm -rf .astro 2>/dev/null || true
|
||||||
|
rm -rf dist 2>/dev/null || true
|
||||||
|
print_success "Astro cache cleared"
|
||||||
|
|
||||||
|
# 4. Clear Docker build cache
|
||||||
|
print_step "4. Clearing Docker build cache..."
|
||||||
|
docker builder prune -a -f 2>/dev/null || print_warning "Docker not available or permission denied"
|
||||||
|
print_success "Docker build cache cleared"
|
||||||
|
|
||||||
|
# 5. Clear Docker containers and images
|
||||||
|
print_step "5. Stopping and removing Docker containers..."
|
||||||
|
docker-compose down 2>/dev/null || true
|
||||||
|
docker system prune -a -f 2>/dev/null || print_warning "Docker system prune failed"
|
||||||
|
print_success "Docker containers and images cleared"
|
||||||
|
|
||||||
|
# 6. Clear any temporary files
|
||||||
|
print_step "6. Clearing temporary files..."
|
||||||
|
rm -rf tmp/ 2>/dev/null || true
|
||||||
|
rm -rf .tmp/ 2>/dev/null || true
|
||||||
|
rm -rf logs/*.log 2>/dev/null || true
|
||||||
|
print_success "Temporary files cleared"
|
||||||
|
|
||||||
|
# 7. Clear Vite cache
|
||||||
|
print_step "7. Clearing Vite cache..."
|
||||||
|
rm -rf node_modules/.vite 2>/dev/null || true
|
||||||
|
print_success "Vite cache cleared"
|
||||||
|
|
||||||
|
# 8. Clear TypeScript cache
|
||||||
|
print_step "8. Clearing TypeScript cache..."
|
||||||
|
rm -rf node_modules/.cache/typescript 2>/dev/null || true
|
||||||
|
print_success "TypeScript cache cleared"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "🎉 All caches cleared successfully!"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Next steps:${NC}"
|
||||||
|
echo "1. Hard refresh your browser (Ctrl+Shift+R or Cmd+Shift+R)"
|
||||||
|
echo "2. Open browser dev tools and disable cache (Network tab)"
|
||||||
|
echo "3. Run: npm run docker:build --no-cache"
|
||||||
|
echo "4. Run: npm run docker:up"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}For persistent cache issues, also try:${NC}"
|
||||||
|
echo "• Clear browser data/cookies for localhost:3000"
|
||||||
|
echo "• Use incognito/private browsing mode"
|
||||||
|
echo "• Add ?v=\$(date +%s) to URLs for cache busting"
|
||||||
93
src/pages/api/admin/activity.ts
Normal file
93
src/pages/api/admin/activity.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
import { createSupabaseAdmin } from '../../../lib/supabase-admin';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request, cookies }) => {
|
||||||
|
try {
|
||||||
|
// Check authentication
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use admin client to bypass RLS
|
||||||
|
const serviceClient = createSupabaseAdmin();
|
||||||
|
|
||||||
|
// Get recent activity data
|
||||||
|
const [eventsResult, ticketsResult, usersResult] = await Promise.all([
|
||||||
|
serviceClient.from('events').select('*, organizations(name)').order('created_at', { ascending: false }).limit(5),
|
||||||
|
serviceClient.from('tickets').select('*, events(title)').order('created_at', { ascending: false }).limit(10),
|
||||||
|
serviceClient.from('users').select('*, organizations(name)').order('created_at', { ascending: false }).limit(5)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const activities = [];
|
||||||
|
|
||||||
|
// Add recent events
|
||||||
|
if (eventsResult.data) {
|
||||||
|
eventsResult.data.forEach(event => {
|
||||||
|
activities.push({
|
||||||
|
type: 'event',
|
||||||
|
title: `New event created: ${event.title}`,
|
||||||
|
subtitle: `by ${event.organizations?.name || 'Unknown'}`,
|
||||||
|
time: new Date(event.created_at),
|
||||||
|
icon: '📅'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add recent users
|
||||||
|
if (usersResult.data) {
|
||||||
|
usersResult.data.forEach(user => {
|
||||||
|
activities.push({
|
||||||
|
type: 'user',
|
||||||
|
title: `New user registered: ${user.name || user.email}`,
|
||||||
|
subtitle: `Organization: ${user.organizations?.name || 'None'}`,
|
||||||
|
time: new Date(user.created_at),
|
||||||
|
icon: '👤'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add recent tickets
|
||||||
|
if (ticketsResult.data) {
|
||||||
|
ticketsResult.data.slice(0, 5).forEach(ticket => {
|
||||||
|
activities.push({
|
||||||
|
type: 'ticket',
|
||||||
|
title: `Ticket sold: $${ticket.price}`,
|
||||||
|
subtitle: `for ${ticket.events?.title || 'Unknown Event'}`,
|
||||||
|
time: new Date(ticket.created_at),
|
||||||
|
icon: '🎫'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by time and take the most recent 10
|
||||||
|
activities.sort((a, b) => b.time - a.time);
|
||||||
|
const recentActivities = activities.slice(0, 10);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: recentActivities
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin activity error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to load recent activity'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
73
src/pages/api/admin/admin-events.ts
Normal file
73
src/pages/api/admin/admin-events.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
import { createSupabaseAdmin } from '../../../lib/supabase-admin';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request, cookies }) => {
|
||||||
|
try {
|
||||||
|
// Check authentication
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use admin client to bypass RLS
|
||||||
|
const serviceClient = createSupabaseAdmin();
|
||||||
|
|
||||||
|
// Get events with organization and user details
|
||||||
|
const { data: events, error } = await serviceClient
|
||||||
|
.from('events')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
organizations(name),
|
||||||
|
users!events_created_by_fkey(name, email)
|
||||||
|
`)
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Events query error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to fetch events'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ticket type counts for each event
|
||||||
|
if (events) {
|
||||||
|
for (const event of events) {
|
||||||
|
const { data: ticketTypes } = await serviceClient
|
||||||
|
.from('ticket_types')
|
||||||
|
.select('id')
|
||||||
|
.eq('event_id', event.id);
|
||||||
|
event.ticket_type_count = ticketTypes ? ticketTypes.length : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: events || []
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin events error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to load events'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
73
src/pages/api/admin/admin-tickets.ts
Normal file
73
src/pages/api/admin/admin-tickets.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
import { createSupabaseAdmin } from '../../../lib/supabase-admin';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request, cookies }) => {
|
||||||
|
try {
|
||||||
|
// Check authentication
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use admin client to bypass RLS
|
||||||
|
const serviceClient = createSupabaseAdmin();
|
||||||
|
|
||||||
|
// Get tickets with event and organization details
|
||||||
|
const { data: tickets, error } = await serviceClient
|
||||||
|
.from('tickets')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
ticket_types (
|
||||||
|
name,
|
||||||
|
price
|
||||||
|
),
|
||||||
|
events (
|
||||||
|
title,
|
||||||
|
venue,
|
||||||
|
start_time,
|
||||||
|
organizations (
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
.order('created_at', { ascending: false })
|
||||||
|
.limit(100);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Tickets query error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to fetch tickets'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: tickets || []
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin tickets error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to load tickets'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
69
src/pages/api/admin/organizations.ts
Normal file
69
src/pages/api/admin/organizations.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
import { createSupabaseAdmin } from '../../../lib/supabase-admin';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request, cookies }) => {
|
||||||
|
try {
|
||||||
|
// Check authentication
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use admin client to bypass RLS
|
||||||
|
const serviceClient = createSupabaseAdmin();
|
||||||
|
|
||||||
|
// Get organizations
|
||||||
|
const { data: orgs, error } = await serviceClient
|
||||||
|
.from('organizations')
|
||||||
|
.select('*')
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Organizations query error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to fetch organizations'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user counts for each organization
|
||||||
|
if (orgs) {
|
||||||
|
for (const org of orgs) {
|
||||||
|
const { data: users } = await serviceClient
|
||||||
|
.from('users')
|
||||||
|
.select('id')
|
||||||
|
.eq('organization_id', org.id);
|
||||||
|
org.user_count = users ? users.length : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: orgs || []
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin organizations error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to load organizations'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
64
src/pages/api/admin/stats.ts
Normal file
64
src/pages/api/admin/stats.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
import { createSupabaseAdmin } from '../../../lib/supabase-admin';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request, cookies }) => {
|
||||||
|
try {
|
||||||
|
// Check authentication
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use admin client to bypass RLS
|
||||||
|
const serviceClient = createSupabaseAdmin();
|
||||||
|
|
||||||
|
// Get platform statistics
|
||||||
|
const [organizationsResult, eventsResult, ticketsResult, usersResult] = await Promise.all([
|
||||||
|
serviceClient.from('organizations').select('id'),
|
||||||
|
serviceClient.from('events').select('id'),
|
||||||
|
serviceClient.from('tickets').select('price'),
|
||||||
|
serviceClient.from('users').select('id')
|
||||||
|
]);
|
||||||
|
|
||||||
|
const organizations = organizationsResult.data?.length || 0;
|
||||||
|
const events = eventsResult.data?.length || 0;
|
||||||
|
const tickets = ticketsResult.data || [];
|
||||||
|
const users = usersResult.data?.length || 0;
|
||||||
|
const ticketCount = tickets.length;
|
||||||
|
const revenue = tickets.reduce((sum, ticket) => sum + (ticket.price || 0), 0);
|
||||||
|
const platformFees = revenue * 0.05; // Assuming 5% platform fee
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
organizations,
|
||||||
|
events,
|
||||||
|
tickets: ticketCount,
|
||||||
|
revenue,
|
||||||
|
platformFees,
|
||||||
|
users
|
||||||
|
}
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin stats error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to load platform statistics'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
89
src/pages/api/user/events.ts
Normal file
89
src/pages/api/user/events.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { verifyAuth } from '../../../lib/auth';
|
||||||
|
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ cookies }) => {
|
||||||
|
try {
|
||||||
|
// Verify authentication using server-side auth
|
||||||
|
const auth = await verifyAuth(cookies);
|
||||||
|
|
||||||
|
if (!auth) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
error: 'Not authenticated'
|
||||||
|
}), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Supabase client for server-side queries
|
||||||
|
const supabase = createSupabaseServerClient(cookies);
|
||||||
|
|
||||||
|
// Get user profile to check if they're admin
|
||||||
|
const { data: userProfile, error: profileError } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('role, organization_id')
|
||||||
|
.eq('id', auth.user.id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (profileError) {
|
||||||
|
console.error('Error loading user profile:', profileError);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
error: 'Error loading user profile'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is admin or has organization_id
|
||||||
|
const isAdmin = userProfile?.role === 'admin';
|
||||||
|
if (!isAdmin && !userProfile?.organization_id) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: []
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load events based on user permissions
|
||||||
|
let query = supabase.from('events').select('*');
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
// Regular users see only their organization's events
|
||||||
|
query = query.eq('organization_id', userProfile.organization_id);
|
||||||
|
}
|
||||||
|
// Admins see all events (no filter)
|
||||||
|
|
||||||
|
const { data: events, error } = await query.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error loading events:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
error: 'Error loading events'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: events || []
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('User events API error:', error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
error: 'Internal server error'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -23,7 +23,7 @@ const search = url.searchParams.get('search');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Event Calendar - Black Canyon Tickets">
|
<Layout title="Event Calendar - Black Canyon Tickets">
|
||||||
<div class="min-h-screen">
|
<div class="min-h-screen" data-theme-background>
|
||||||
<!-- Hero Section with Dynamic Background -->
|
<!-- Hero Section with Dynamic Background -->
|
||||||
<section id="hero-section" class="relative overflow-hidden sticky top-0 z-40" style="background: var(--bg-gradient);">
|
<section id="hero-section" class="relative overflow-hidden sticky top-0 z-40" style="background: var(--bg-gradient);">
|
||||||
<PublicHeader showCalendarNav={true} />
|
<PublicHeader showCalendarNav={true} />
|
||||||
@@ -406,6 +406,9 @@ const search = url.searchParams.get('search');
|
|||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Import glassmorphism theme styles */
|
||||||
|
@import '../styles/glassmorphism.css';
|
||||||
|
|
||||||
/* Custom animations */
|
/* Custom animations */
|
||||||
@keyframes float {
|
@keyframes float {
|
||||||
0%, 100% { transform: translateY(0px); }
|
0%, 100% { transform: translateY(0px); }
|
||||||
@@ -502,14 +505,46 @@ const search = url.searchParams.get('search');
|
|||||||
<script>
|
<script>
|
||||||
console.log('=== CALENDAR SCRIPT STARTING ===');
|
console.log('=== CALENDAR SCRIPT STARTING ===');
|
||||||
|
|
||||||
// Simple theme toggle function
|
// Theme management system
|
||||||
window.toggleTheme = function() {
|
function getCurrentTheme() {
|
||||||
const html = document.documentElement;
|
if (typeof window === 'undefined') return 'dark';
|
||||||
const currentTheme = html.getAttribute('data-theme') || 'light';
|
|
||||||
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
||||||
|
|
||||||
html.setAttribute('data-theme', newTheme);
|
const savedTheme = localStorage.getItem('theme');
|
||||||
localStorage.setItem('theme', newTheme);
|
if (savedTheme) return savedTheme;
|
||||||
|
|
||||||
|
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTheme(theme) {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
document.documentElement.classList.remove('light', 'dark');
|
||||||
|
document.documentElement.classList.add(theme);
|
||||||
|
|
||||||
|
document.body.classList.remove('light', 'dark');
|
||||||
|
document.body.classList.add(theme);
|
||||||
|
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
|
||||||
|
// Dispatch theme change event
|
||||||
|
window.dispatchEvent(new CustomEvent('themeChanged', {
|
||||||
|
detail: { theme }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeTheme() {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
const savedTheme = getCurrentTheme();
|
||||||
|
setTheme(savedTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme toggle function
|
||||||
|
window.toggleTheme = function() {
|
||||||
|
const currentTheme = getCurrentTheme();
|
||||||
|
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||||
|
setTheme(newTheme);
|
||||||
|
|
||||||
// Update icon
|
// Update icon
|
||||||
const toggle = document.getElementById('theme-toggle');
|
const toggle = document.getElementById('theme-toggle');
|
||||||
@@ -523,12 +558,30 @@ const search = url.searchParams.get('search');
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('Theme toggled to:', newTheme);
|
console.log('Theme toggled to:', newTheme);
|
||||||
|
return newTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize theme on page load
|
// Initialize theme immediately
|
||||||
const savedTheme = localStorage.getItem('theme') ||
|
initializeTheme();
|
||||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
||||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
// Handle theme changes and update background
|
||||||
|
function updateBackground() {
|
||||||
|
const theme = getCurrentTheme();
|
||||||
|
const bgElement = document.querySelector('[data-theme-background]');
|
||||||
|
if (bgElement) {
|
||||||
|
if (theme === 'light') {
|
||||||
|
bgElement.style.background = '#f8fafc';
|
||||||
|
} else {
|
||||||
|
bgElement.style.background = 'var(--bg-gradient)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for theme changes
|
||||||
|
window.addEventListener('themeChanged', updateBackground);
|
||||||
|
|
||||||
|
// Initial background update
|
||||||
|
updateBackground();
|
||||||
|
|
||||||
// Import geolocation utilities - get from environment or default to empty
|
// Import geolocation utilities - get from environment or default to empty
|
||||||
const MAPBOX_TOKEN = '';
|
const MAPBOX_TOKEN = '';
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ if (!auth) {
|
|||||||
<span id="view-text">Calendar View</span>
|
<span id="view-text">Calendar View</span>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href="/calendar"
|
href="/settings/fees"
|
||||||
class="glass-card px-6 py-3 rounded-xl text-sm font-medium transition-all duration-200 shadow-lg flex items-center gap-2 hover:shadow-xl hover:scale-105 hover:opacity-80"
|
class="glass-card px-6 py-3 rounded-xl text-sm font-medium transition-all duration-200 shadow-lg flex items-center gap-2 hover:shadow-xl hover:scale-105 hover:opacity-80"
|
||||||
style="color: var(--glass-text-primary);"
|
style="color: var(--glass-text-primary);"
|
||||||
>
|
>
|
||||||
@@ -291,6 +291,7 @@ if (!auth) {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { supabase } from '../lib/supabase';
|
import { supabase } from '../lib/supabase';
|
||||||
|
import { ApiRouter } from '../lib/api-router';
|
||||||
|
|
||||||
const eventsContainer = document.getElementById('events-container');
|
const eventsContainer = document.getElementById('events-container');
|
||||||
const loading = document.getElementById('loading');
|
const loading = document.getElementById('loading');
|
||||||
@@ -437,21 +438,8 @@ if (!auth) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load events based on user permissions
|
// Load events using the API router to avoid CORS issues
|
||||||
let query = supabase.from('events').select('*');
|
const events = await ApiRouter.loadUserEvents();
|
||||||
|
|
||||||
if (!isAdmin) {
|
|
||||||
// Regular users see only their organization's events
|
|
||||||
query = query.eq('organization_id', userProfile.organization_id);
|
|
||||||
}
|
|
||||||
// Admins see all events (no filter)
|
|
||||||
|
|
||||||
const { data: events, error } = await query.order('created_at', { ascending: false });
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
// Error loading events from database
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Successfully loaded events
|
// Successfully loaded events
|
||||||
allEvents = events || [];
|
allEvents = events || [];
|
||||||
|
|||||||
Reference in New Issue
Block a user