first working demo
This commit is contained in:
114
src/routes/participate/+page.server.js
Normal file
114
src/routes/participate/+page.server.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db/index.js';
|
||||
import { inviteLink, participant, audioFile, rating } from '$lib/server/db/schema.js';
|
||||
import { eq, isNull, and } from 'drizzle-orm';
|
||||
|
||||
export async function load({ url, cookies }) {
|
||||
const token = url.searchParams.get('token');
|
||||
|
||||
if (!token) {
|
||||
throw error(400, 'Invalid or missing invite token');
|
||||
}
|
||||
|
||||
const invites = await db.select().from(inviteLink).where(
|
||||
and(
|
||||
eq(inviteLink.token, token),
|
||||
isNull(inviteLink.deletedAt)
|
||||
)
|
||||
);
|
||||
|
||||
if (invites.length === 0) {
|
||||
throw error(404, 'Invite link not found or has been deleted');
|
||||
}
|
||||
|
||||
const invite = invites[0];
|
||||
|
||||
let participantId = cookies.get(`participant-${token}`);
|
||||
let isExistingParticipant = false;
|
||||
|
||||
if (participantId) {
|
||||
const participants = await db
|
||||
.select()
|
||||
.from(participant)
|
||||
.where(
|
||||
and(
|
||||
eq(participant.id, participantId),
|
||||
isNull(participant.deletedAt)
|
||||
)
|
||||
);
|
||||
isExistingParticipant = participants.length > 0;
|
||||
}
|
||||
|
||||
if (!isExistingParticipant) {
|
||||
participantId = crypto.randomUUID();
|
||||
|
||||
await db.insert(participant).values({
|
||||
id: participantId,
|
||||
inviteToken: token,
|
||||
sessionId: null,
|
||||
createdAt: new Date()
|
||||
});
|
||||
|
||||
await db
|
||||
.update(inviteLink)
|
||||
.set({
|
||||
isUsed: true,
|
||||
usedAt: new Date()
|
||||
})
|
||||
.where(eq(inviteLink.token, token));
|
||||
|
||||
cookies.set(`participant-${token}`, participantId, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 24 * 30
|
||||
});
|
||||
}
|
||||
|
||||
const audioFiles = await db.select({
|
||||
id: audioFile.id,
|
||||
filename: audioFile.filename,
|
||||
contentType: audioFile.contentType,
|
||||
duration: audioFile.duration,
|
||||
fileSize: audioFile.fileSize,
|
||||
createdAt: audioFile.createdAt
|
||||
})
|
||||
.from(audioFile)
|
||||
.where(isNull(audioFile.deletedAt)); // Only show active audio files
|
||||
|
||||
// Get completed ratings for this participant (only active, non-deleted ratings for active audio files)
|
||||
const completedRatings = await db
|
||||
.select({
|
||||
audioFileId: rating.audioFileId
|
||||
})
|
||||
.from(rating)
|
||||
.innerJoin(audioFile, and(
|
||||
eq(rating.audioFileId, audioFile.id),
|
||||
isNull(audioFile.deletedAt) // Only count ratings for active audio files
|
||||
))
|
||||
.where(
|
||||
and(
|
||||
eq(rating.participantId, participantId),
|
||||
eq(rating.isCompleted, true),
|
||||
isNull(rating.deletedAt) // Only count active ratings
|
||||
)
|
||||
);
|
||||
|
||||
const completedAudioIds = new Set(completedRatings.map(r => r.audioFileId));
|
||||
|
||||
// Add completion status to audio files
|
||||
const audioFilesWithStatus = audioFiles.map(file => ({
|
||||
...file,
|
||||
isCompleted: completedAudioIds.has(file.id)
|
||||
}));
|
||||
|
||||
return {
|
||||
invite,
|
||||
participantId,
|
||||
audioFiles: audioFilesWithStatus,
|
||||
token,
|
||||
completedCount: completedRatings.length,
|
||||
totalCount: audioFiles.length
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user