feat(tmp): invite link feature

This commit is contained in:
Levente Orban
2025-10-15 10:00:26 +02:00
parent f6b51232a7
commit c9c78d0ea6
18 changed files with 1199 additions and 164 deletions

View File

@@ -1,5 +1,5 @@
import { database } from '$lib/database/db';
import { events } from '$lib/database/schema';
import { events, inviteTokens } from '$lib/database/schema';
import { eq, and } from 'drizzle-orm';
import { fail, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
@@ -23,8 +23,29 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
throw redirect(303, '/event');
}
// Fetch invite token if this is an invite-only event
let inviteToken = null;
if (event[0].visibility === 'invite-only') {
const tokenData = await database
.select()
.from(inviteTokens)
.where(eq(inviteTokens.eventId, eventId))
.limit(1);
if (tokenData.length > 0) {
inviteToken = {
id: tokenData[0].id,
event_id: tokenData[0].eventId,
token: tokenData[0].token,
expires_at: tokenData[0].expiresAt.toISOString(),
created_at: tokenData[0].createdAt?.toISOString() || new Date().toISOString()
};
}
}
return {
event: event[0]
event: event[0],
inviteToken
};
};

View File

@@ -21,6 +21,9 @@
let errors: Record<string, string> = {};
let isSubmitting = false;
let inviteToken = data.inviteToken;
let inviteLinkCopied = false;
// Get today's date in YYYY-MM-DD format for min attribute
const today = new Date().toISOString().split('T')[0];
@@ -67,6 +70,21 @@
const handleCancel = () => {
goto(`/event/${data.event.id}`);
};
const copyInviteLink = async () => {
if (inviteToken) {
const inviteUrl = `${window.location.origin}/event/${data.event.id}/invite/${inviteToken.token}`;
try {
await navigator.clipboard.writeText(inviteUrl);
inviteLinkCopied = true;
setTimeout(() => {
inviteLinkCopied = false;
}, 3000);
} catch (err) {
console.error('Failed to copy invite link:', err);
}
}
};
</script>
<svelte:head>
@@ -315,7 +333,7 @@
<legend class="text-dark-800 mb-3 block text-sm font-semibold">
{t('common.visibility')} <span class="text-red-400">{t('common.required')}</span>
</legend>
<div class="grid grid-cols-2 gap-3">
<div class="grid grid-cols-3 gap-3">
<button
type="button"
class="rounded-sm border-2 px-4 py-3 font-medium transition-all duration-200 {eventData.visibility ===
@@ -336,15 +354,59 @@
>
{t('create.privateOption')}
</button>
<button
type="button"
class="rounded-sm border-2 px-4 py-3 font-medium transition-all duration-200 {eventData.visibility ===
'invite-only'
? ' border-amber-500 bg-amber-400/20 font-semibold hover:bg-amber-400/70'
: 'border-dark-300 text-dark-700 bg-gray-600/20 hover:bg-gray-600/70'}"
on:click={() => (eventData.visibility = 'invite-only')}
>
{t('create.inviteOnlyOption')}
</button>
</div>
<p class="mt-2 text-xs text-slate-400">
{eventData.visibility === 'public'
? t('create.publicDescription')
: t('create.privateDescription')}
: eventData.visibility === 'private'
? t('create.privateDescription')
: t('create.inviteOnlyDescription')}
</p>
</fieldset>
</div>
<!-- Invite Link Section (only for invite-only events) -->
{#if eventData.visibility === 'invite-only' && inviteToken}
<div class="rounded-sm border border-amber-500/30 bg-amber-900/20 p-4">
<div class="mb-3 flex items-center justify-between">
<h3 class="text-lg font-semibold text-amber-400">Invite Link</h3>
</div>
<div class="space-y-3">
<div class="flex items-center space-x-2">
<input
type="text"
value={`${window.location.origin}/event/${data.event.id}/invite/${inviteToken.token}`}
readonly
class="flex-1 rounded-sm border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-900"
/>
<button
type="button"
on:click={copyInviteLink}
class="rounded-sm border border-amber-300 bg-amber-200 px-3 py-2 text-sm font-medium text-amber-900 hover:bg-amber-300"
>
{inviteLinkCopied ? t('common.success') : t('common.copyLink')}
</button>
</div>
<p class="text-xs text-amber-300">
{t('event.inviteLinkExpiresAt', {
time: new Date(inviteToken.expires_at).toLocaleString()
})}
</p>
</div>
</div>
{/if}
<!-- Action Buttons -->
<div class="flex space-x-3">
<button