diff --git a/client/admin.js b/client/admin.js index 766057d..b40bbae 100644 --- a/client/admin.js +++ b/client/admin.js @@ -454,7 +454,8 @@ async function loadInvites() { ${status} ${invite.is_active && !isExpired && invite.remaining_uses > 0 - ? `` + ? ` + ` : '-' } @@ -619,6 +620,16 @@ async function handleCreateInvite() { } } +function copyInviteLink(code) { + const link = `${window.location.origin}/?invite=${encodeURIComponent(code)}`; + navigator.clipboard.writeText(link).then(() => { + showToast('Invite link copied!', 'success'); + }).catch(() => { + // Fallback: select text for manual copy + prompt('Copy this link:', link); + }); +} + async function promptRevokeInvite(code) { if (!confirm(`Are you sure you want to revoke invite code ${code}?`)) return; diff --git a/client/app.js b/client/app.js index 94d8e67..4e22926 100644 --- a/client/app.js +++ b/client/app.js @@ -4672,8 +4672,9 @@ class AuthManager { this.forgotForm?.addEventListener('submit', (e) => this.handleForgotPassword(e)); this.resetForm?.addEventListener('submit', (e) => this.handleResetPassword(e)); - // Check URL for reset token on page load + // Check URL for reset token or invite code on page load this.checkResetToken(); + this.checkInviteCode(); } showModal(form = 'login') { @@ -4826,6 +4827,18 @@ class AuthManager { } } + checkInviteCode() { + const params = new URLSearchParams(window.location.search); + const invite = params.get('invite'); + + if (invite) { + this.signupInviteCode.value = invite; + this.showModal('signup'); + // Clean URL + window.history.replaceState({}, '', '/'); + } + } + async handleForgotPassword(e) { e.preventDefault(); this.clearErrors();