Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull Request Overview
This PR implements a comprehensive invite system for the Smart Todo platform, integrating invite flows for both platform-level and stake-specific invitations. The changes enable users to invite friends via unique codes, track invitation metrics, and accept invitations during signup or while logged in.
Key Changes:
- Added complete invite API infrastructure with code generation, lookup, acceptance, and statistics endpoints
- Integrated invite codes into the signup/registration flow with automatic invitation acceptance
- Enhanced social sharing with security codes for more secure and trackable stake invitations
- Added UI components for managing invitations and displaying invite landing pages
Reviewed Changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 25 comments.
Show a summary per file
| File | Description |
|---|---|
src/app/api/auth/register/route.ts |
Extended registration to accept and validate invite codes, automatically linking new users to their inviters |
src/app/api/invite/[code]/route.ts |
Unified endpoint to fetch invitation details for both platform and stake invitations by code |
src/app/api/invite/[code]/accept/route.ts |
Handles platform invitation acceptance with validation and referral tracking |
src/app/api/invite/code/route.ts |
Generates and retrieves user referral codes, creates platform invitations |
src/app/api/invite/invitations/route.ts |
CRUD operations for managing user's sent invitations (list, resend, cancel) |
src/app/api/invite/stats/route.ts |
Provides referral statistics including invitation counts, acceptance rates, and view metrics |
src/app/api/stakes/[id]/share/route.ts |
Changed from POST to GET; returns shareable links with security codes for stake invitations |
src/app/api/stakes/route.ts |
Pre-generates public stake invitations with security codes during SOCIAL_STAKE creation |
src/app/invite/[code]/page.tsx |
Unified landing page that routes to appropriate invite component based on invitation type |
src/app/stakes/invite/[id]/page.tsx |
Redirects legacy stake invite URLs to new unified invite route using security codes |
src/app/auth/signup/page.tsx |
Captures invite codes from URL parameters and passes them to registration endpoint |
src/components/invite/InviteLandingClient.tsx |
Client component for stake invitation landing pages with auto-accept functionality |
src/components/invite/PlatformInviteLanding.tsx |
Client component for platform invitation landing pages with authentication flow |
src/components/settings/InvitationManagement.tsx |
New UI for managing sent invitations with filtering, resending, and cancellation |
src/components/settings/InviteFriendsSettings.tsx |
Updated to fetch real stats from API and embed invitation management component |
src/components/stakes/EnhancedSocialShare.tsx |
Fetches and uses security codes for generating shareable stake URLs |
src/components/stakes/EnhancedUniversalShareModal.tsx |
Updated to use security codes in QR codes, direct links, and embed codes |
src/lib/social-share-service.ts |
Enhanced to generate URLs using security codes when available, falling back to stake IDs |
.gitignore |
Added documentation file to ignore list |
Comments suppressed due to low confidence (1)
src/components/settings/InvitationManagement.tsx:59
- Unused variable url.
const url = type ? `/api/invite/invitations?type=${type}` : '/api/invite/invitations?type=PLATFORM';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| useEffect(() => { | ||
| if (typeof window !== "undefined" && session && invitation?.status === 'PENDING') { | ||
| const params = new URLSearchParams(window.location.search); | ||
| if (params.get("autoAccept") === "1" && !accepting) { | ||
| handleAccept(); | ||
| } | ||
| } | ||
| }, [session, invitation]); |
There was a problem hiding this comment.
The useEffect has a missing dependency: handleAccept. Either include it in the dependency array or wrap the function in useCallback. Additionally, the dependency array [session, invitation] should also include accepting to prevent calling handleAccept multiple times while a request is in progress.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| data: { | ||
| stakeId: stake.id, | ||
| inviterId: stake.userId, | ||
| inviteeEmail: 'public@share.com', // Placeholder for public shares |
There was a problem hiding this comment.
The placeholder email 'public@share.com' is hardcoded here, which creates a magic string that's repeated across multiple files (also in /api/stakes/route.ts and /api/stakes/[id]/share/route.ts). This should be extracted to a constant in a shared location to improve maintainability and reduce the risk of typos.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
src/app/invite/[code]/page.tsx
Outdated
| const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || process.env.VERCEL_URL || 'http://localhost:3000'; | ||
|
|
There was a problem hiding this comment.
The server component fetches data using fetch with baseUrl constructed from environment variables. However, on server-side, NEXT_PUBLIC_BASE_URL or VERCEL_URL might not include the protocol (http/https), especially for VERCEL_URL. This could result in malformed URLs like vercel-deployment.vercel.app/api/invite/... instead of https://vercel-deployment.vercel.app/api/invite/.... Consider adding protocol handling or using a more robust URL construction method.
| const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || process.env.VERCEL_URL || 'http://localhost:3000'; | |
| let baseUrl = process.env.NEXT_PUBLIC_BASE_URL || process.env.VERCEL_URL || 'http://localhost:3000'; | |
| // Ensure baseUrl includes protocol | |
| if (!/^https?:\/\//i.test(baseUrl)) { | |
| baseUrl = `https://${baseUrl}`; | |
| } |
|
|
||
| await prisma.stakeInvitation.update({ | ||
| where: { id: invitationId }, | ||
| data: { status: 'DECLINED' } // Use DECLINED for cancelled stake invitations |
There was a problem hiding this comment.
The status update to 'DECLINED' is used for cancelled stake invitations, but 'DECLINED' typically means the invitee rejected the invitation, not that the inviter cancelled it. Consider using 'CANCELLED' for consistency with platform invitations, or ensure the schema supports 'CANCELLED' status for stake invitations as well.
| data: { status: 'DECLINED' } // Use DECLINED for cancelled stake invitations | |
| data: { status: 'CANCELLED' } // Use CANCELLED for cancelled stake invitations |
| // Check if this invitation was already accepted by someone else | ||
| if (invitation.acceptedBy) { |
There was a problem hiding this comment.
This check prevents multiple users from accepting the same invitation, but it doesn't handle the case where inviteeEmail is set to a specific email. Platform invitations can be accepted by anyone if no inviteeEmail is specified, but when an inviteeEmail is set, only one person should accept it. However, the current logic allows the same invitation code to be accepted only once regardless of whether it was targeted. Consider whether this is the intended behavior - if invitations without inviteeEmail should be reusable codes, this check would prevent that.
| // Check if this invitation was already accepted by someone else | |
| if (invitation.acceptedBy) { | |
| // For invitations with inviteeEmail, only allow one acceptance | |
| if (invitation.inviteeEmail && invitation.acceptedBy) { |
| "use client"; | ||
|
|
||
| import React, { useState, useEffect } from "react"; | ||
| import { signIn, useSession } from "next-auth/react"; |
There was a problem hiding this comment.
Unused import signIn.
| import { signIn, useSession } from "next-auth/react"; | |
| import { useSession } from "next-auth/react"; |
| } | ||
|
|
||
| export default function PlatformInviteLanding({ invitation, error }: PlatformInviteLandingProps) { | ||
| const { data: session, status: sessionStatus } = useSession(); |
There was a problem hiding this comment.
Unused variable sessionStatus.
| const { data: session, status: sessionStatus } = useSession(); | |
| const { data: session } = useSession(); |
| export default function PlatformInviteLanding({ invitation, error }: PlatformInviteLandingProps) { | ||
| const { data: session, status: sessionStatus } = useSession(); | ||
| const router = useRouter(); | ||
| const [loading, setLoading] = useState(false); |
There was a problem hiding this comment.
Unused variable loading.
| const [loading, setLoading] = useState(false); |
| export default function PlatformInviteLanding({ invitation, error }: PlatformInviteLandingProps) { | ||
| const { data: session, status: sessionStatus } = useSession(); | ||
| const router = useRouter(); | ||
| const [loading, setLoading] = useState(false); |
There was a problem hiding this comment.
Unused variable setLoading.
| const [loading, setLoading] = useState(false); |
| TrashIcon, | ||
| EyeIcon | ||
| } from "@heroicons/react/24/outline"; | ||
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; |
There was a problem hiding this comment.
Unused imports CardDescription, CardHeader, CardTitle.
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Card, CardContent } from "@/components/ui/card"; |
|
@chemist-god I've opened a new pull request, #55, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@chemist-god I've opened a new pull request, #56, to work on those changes. Once the pull request is ready, I'll request review from you. |
Invite & Auth: invite APIs, landing pages, and social-share improvements
Summary
This PR implements a complete invite flow (API endpoints, landing pages, and client components), updates authentication/signup to integrate invites, enhances stake-sharing endpoints, and improves social sharing UI and service logic. These changes enable invite-based onboarding, better share metadata/links across platforms, and new UI for managing invitations.
What changed (high level)
Backend / API
src/app/api/auth/register/route.ts— update/add registration behavior to support invite associationsrc/app/api/invite/[code]/accept/route.ts— accept invite by codesrc/app/api/invite/[code]/route.ts— lookup invite details by codesrc/app/api/invite/code/route.ts— generate invite codessrc/app/api/invite/invitations/route.ts— create/list invitationssrc/app/api/invite/stats/route.ts— invite metrics endpointsrc/app/api/stakes/[id]/share/route.ts— improve stake share endpoint (metadata / platform links)src/app/api/stakes/route.ts— stakes collection improvements for invites/sharingFrontend / Pages / Components
src/app/invite/[code]/page.tsx— invite landing pagesrc/app/stakes/invite/[id]/page.tsx— stake invite page updatessrc/app/auth/signup/page.tsx— signup adjustments to accept invitessrc/components/invite/InviteLandingClient.tsxsrc/components/invite/PlatformInviteLanding.tsxsrc/components/settings/InvitationManagement.tsxsrc/components/settings/InviteFriendsSettings.tsxsrc/components/stakes/EnhancedSocialShare.tsxsrc/components/stakes/EnhancedUniversalShareModal.tsxLibraries / Services
src/lib/social-share-service.ts— updated logic for building share metadata, platform links, and invite-aware sharing behaviorWhy
How to test (smoke checklist)
Start dev server:
npm run devInvite API checks
code.Invite landing page
http://localhost:3000/invite/<code>in a browser.Signup flow
/auth/signupwhile carrying an invite code; ensure the created account is associated with the invite (or the UI highlights invite benefits).Share & stake flows
/api/stakes/[id]/share) returns proper metadata.EnhancedSocialShareandEnhancedUniversalShareModalrender correctly and produce expected share data.UI sanity checks
InvitationManagementandInviteFriendsSettingspages; verify lists, actions (revoke/resend), and toggles work.Lint & tests
npm run lintnpm test(if available)Database / Migrations
npx prisma generatenpx prisma db pushnpx prisma migrate deployIf any DB errors occur, report them and we can prepare a migration or revert strategy.
Review notes / Focus areas