40c56770a2
- Added AdminBlocksManagement component for managing court blocks. - Implemented functionality to create, edit, and delete blocks. - Integrated fetching of courts and blocks from the API. - Added validation for block creation and editing forms. - Enhanced UI with responsive design for mobile and desktop views. - Created database migration for court_blocks table and updated users table with theme_preference.
177 lines
5.0 KiB
TypeScript
177 lines
5.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { db } from '@/lib/db';
|
|
import { courtBlocks, courts, users, announcements } from '@/lib/db/schema';
|
|
import { eq, gte, asc } from 'drizzle-orm';
|
|
import { getSession } from '@/lib/session';
|
|
import { logActivity, ACTIONS, ENTITY_TYPES } from '@/lib/activity-logger';
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const session = await getSession();
|
|
if (!session || session.role !== 'admin') {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const { searchParams } = new URL(request.url);
|
|
const includeExpired = searchParams.get('includeExpired') === 'true';
|
|
|
|
// Get today's date
|
|
const today = new Date().toISOString().split('T')[0];
|
|
|
|
// Fetch blocks with court and creator info
|
|
let query = db
|
|
.select({
|
|
id: courtBlocks.id,
|
|
courtId: courtBlocks.courtId,
|
|
date: courtBlocks.date,
|
|
startTime: courtBlocks.startTime,
|
|
endTime: courtBlocks.endTime,
|
|
reason: courtBlocks.reason,
|
|
createdBy: courtBlocks.createdBy,
|
|
createdAt: courtBlocks.createdAt,
|
|
court: {
|
|
id: courts.id,
|
|
name: courts.name,
|
|
},
|
|
creator: {
|
|
id: users.id,
|
|
name: users.name,
|
|
surname: users.surname,
|
|
},
|
|
})
|
|
.from(courtBlocks)
|
|
.leftJoin(courts, eq(courtBlocks.courtId, courts.id))
|
|
.innerJoin(users, eq(courtBlocks.createdBy, users.id));
|
|
|
|
const rawBlocks = includeExpired
|
|
? await query.orderBy(asc(courtBlocks.date), asc(courtBlocks.startTime))
|
|
: await query
|
|
.where(gte(courtBlocks.date, today))
|
|
.orderBy(asc(courtBlocks.date), asc(courtBlocks.startTime));
|
|
|
|
// Transform to flat structure for frontend
|
|
const blocks = rawBlocks.map((block) => ({
|
|
id: block.id,
|
|
courtId: block.courtId,
|
|
courtName: block.court?.name || null,
|
|
date: block.date,
|
|
startTime: block.startTime,
|
|
endTime: block.endTime,
|
|
reason: block.reason,
|
|
createdBy: block.createdBy,
|
|
creatorName: block.creator ? `${block.creator.name} ${block.creator.surname}` : 'Unknown',
|
|
createdAt: block.createdAt,
|
|
}));
|
|
|
|
return NextResponse.json({ blocks });
|
|
} catch (error) {
|
|
console.error('Error fetching blocks:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const session = await getSession();
|
|
if (!session || session.role !== 'admin') {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const { courtId, date, startTime, endTime, reason, createAnnouncement } = await request.json();
|
|
|
|
if (!date || !startTime || !endTime || !reason) {
|
|
return NextResponse.json(
|
|
{ error: 'Missing required fields: date, startTime, endTime, reason' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validate date is not in the past
|
|
const blockDate = new Date(date);
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
if (blockDate < today) {
|
|
return NextResponse.json({ error: 'Cannot create blocks for past dates' }, { status: 400 });
|
|
}
|
|
|
|
// Get court name if courtId is provided
|
|
let courtName = 'All Courts';
|
|
if (courtId) {
|
|
const court = await db.select().from(courts).where(eq(courts.id, courtId)).limit(1);
|
|
if (court.length === 0) {
|
|
return NextResponse.json({ error: 'Court not found' }, { status: 400 });
|
|
}
|
|
courtName = court[0].name;
|
|
}
|
|
|
|
// Create the block
|
|
const blockId = crypto.randomUUID();
|
|
const [newBlock] = await db
|
|
.insert(courtBlocks)
|
|
.values({
|
|
id: blockId,
|
|
courtId: courtId || null, // null means all courts
|
|
date,
|
|
startTime,
|
|
endTime,
|
|
reason,
|
|
createdBy: session.userId,
|
|
createdAt: new Date(),
|
|
})
|
|
.returning();
|
|
|
|
// Log activity
|
|
await logActivity({
|
|
userId: session.userId,
|
|
action: ACTIONS.BLOCK_CREATE,
|
|
entityType: ENTITY_TYPES.COURT_BLOCK,
|
|
entityId: blockId,
|
|
details: {
|
|
courtId: courtId || 'all',
|
|
date,
|
|
startTime,
|
|
endTime,
|
|
reason,
|
|
},
|
|
});
|
|
|
|
// Optionally create an announcement that expires when the block ends
|
|
let announcementCreated = false;
|
|
if (createAnnouncement) {
|
|
const formattedDate = new Date(date).toLocaleDateString('en-IE', {
|
|
weekday: 'long',
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric',
|
|
});
|
|
|
|
// Set expiry to end of block day at the end time
|
|
const expiryDate = new Date(date);
|
|
const [endHour] = endTime.split(':').map(Number);
|
|
expiryDate.setHours(endHour, 0, 0, 0);
|
|
|
|
await db.insert(announcements).values({
|
|
id: crypto.randomUUID(),
|
|
title: `${reason} - ${formattedDate}`,
|
|
content: `${courtName} will be unavailable on ${formattedDate} from ${startTime} to ${endTime} due to: ${reason}`,
|
|
priority: 'high',
|
|
expiresAt: expiryDate,
|
|
isActive: true,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
announcementCreated = true;
|
|
}
|
|
|
|
return NextResponse.json({
|
|
block: newBlock,
|
|
announcementCreated,
|
|
message: 'Block created successfully',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error creating block:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|