11import { useState , useEffect , useCallback } from 'react' ;
22import { cn } from '../../utils/cn' ;
33import { useLanguage } from '../../contexts/LanguageContext' ;
4- import { Plus , User , Mail , Shield , Trash2 , Check , Loader2 } from 'lucide-react' ;
4+ import { Plus , User , Mail , Shield , Trash2 , Check , Loader2 , Link as LinkIcon , Copy } from 'lucide-react' ;
55import { motion , AnimatePresence } from 'framer-motion' ;
66import { supabase } from '../../config/supabase' ;
77import { useAuth } from '../../contexts/AuthContext' ;
@@ -24,6 +24,9 @@ export default function TeamView({ isDark = true }: { isDark?: boolean }) {
2424 const [ inviteSuccess , setInviteSuccess ] = useState ( false ) ;
2525 const [ members , setMembers ] = useState < TeamMember [ ] > ( [ ] ) ;
2626 const [ isLoadingMembers , setIsLoadingMembers ] = useState ( true ) ;
27+ const [ inviteLink , setInviteLink ] = useState < string | null > ( null ) ;
28+ const [ copiedInvite , setCopiedInvite ] = useState ( false ) ;
29+ const [ currentTeamId , setCurrentTeamId ] = useState < string | null > ( null ) ;
2730
2831 const getAuthHeaders = async ( ) => {
2932 const { data : { session } } = await supabase . auth . getSession ( ) ;
@@ -130,6 +133,40 @@ export default function TeamView({ isDark = true }: { isDark?: boolean }) {
130133 }
131134 } ;
132135
136+ const createInviteLink = async ( ) => {
137+ if ( ! currentTeamId ) {
138+ console . error ( 'No team ID available' ) ;
139+ return ;
140+ }
141+
142+ try {
143+ const headers = await getAuthHeaders ( ) ;
144+ const res = await fetch ( `${ import . meta. env . VITE_SUPABASE_URL } /functions/v1/team-invites` , {
145+ method : 'POST' ,
146+ headers,
147+ body : JSON . stringify ( {
148+ team_id : currentTeamId ,
149+ days_valid : 7 ,
150+ } ) ,
151+ } ) ;
152+
153+ const result = await res . json ( ) ;
154+ if ( res . ok && result . invite_url ) {
155+ setInviteLink ( result . invite_url ) ;
156+ }
157+ } catch ( err ) {
158+ console . error ( 'Failed to create invite link:' , err ) ;
159+ }
160+ } ;
161+
162+ const copyInviteLink = ( ) => {
163+ if ( inviteLink ) {
164+ navigator . clipboard . writeText ( inviteLink ) ;
165+ setCopiedInvite ( true ) ;
166+ setTimeout ( ( ) => setCopiedInvite ( false ) , 2000 ) ;
167+ }
168+ } ;
169+
133170 return (
134171 < div className = "space-y-6 max-w-[1600px] mx-auto pb-10" >
135172 { /* Header */ }
@@ -223,6 +260,20 @@ export default function TeamView({ isDark = true }: { isDark?: boolean }) {
223260 </ >
224261 ) }
225262 </ button >
263+
264+ < button
265+ type = "button"
266+ onClick = { createInviteLink }
267+ className = { cn (
268+ "py-2.5 px-6 rounded-lg font-medium transition-all flex items-center justify-center gap-2 min-w-[140px]" ,
269+ isDark
270+ ? "bg-purple-600 text-white hover:bg-purple-700"
271+ : "bg-purple-500 text-white hover:bg-purple-600"
272+ ) }
273+ >
274+ < LinkIcon className = "w-4 h-4" />
275+ < span > Create Link</ span >
276+ </ button >
226277 </ form >
227278 </ div >
228279 </ div >
@@ -337,6 +388,57 @@ export default function TeamView({ isDark = true }: { isDark?: boolean }) {
337388 </ table >
338389 </ div >
339390 </ div >
391+
392+ { /* Invite Link Modal */ }
393+ { inviteLink && (
394+ < div className = "fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60" >
395+ < div className = { cn (
396+ "rounded-xl p-6 max-w-md" ,
397+ isDark ? "bg-[#0A0A0A] border border-white/10" : "bg-white border border-black/10"
398+ ) } >
399+ < h3 className = { cn ( "text-lg font-semibold mb-2" , isDark ? "text-white" : "text-black" ) } >
400+ Share Invite Link
401+ </ h3 >
402+ < p className = { cn ( "text-sm mb-4" , isDark ? "text-white/60" : "text-gray-600" ) } >
403+ This link expires in 7 days. Share it with team members to join.
404+ </ p >
405+ < div className = { cn (
406+ "p-3 rounded-lg mb-4 flex items-center gap-2" ,
407+ isDark ? "bg-white/5 border border-white/10" : "bg-gray-50 border border-gray-200"
408+ ) } >
409+ < input
410+ type = "text"
411+ value = { inviteLink }
412+ readOnly
413+ className = { cn (
414+ "flex-1 bg-transparent text-xs outline-none" ,
415+ isDark ? "text-white/80" : "text-black"
416+ ) }
417+ />
418+ < button
419+ onClick = { copyInviteLink }
420+ className = { cn (
421+ "p-2 rounded transition-colors" ,
422+ copiedInvite
423+ ? ( isDark ? "bg-green-500/20 text-green-400" : "bg-green-50 text-green-600" )
424+ : ( isDark ? "hover:bg-white/10 text-white/60" : "hover:bg-gray-100 text-gray-600" )
425+ ) }
426+ >
427+ { copiedInvite ? < Check className = "w-4 h-4" /> : < Copy className = "w-4 h-4" /> }
428+ </ button >
429+ </ div >
430+ < button
431+ onClick = { ( ) => setInviteLink ( null ) }
432+ className = { cn (
433+ "w-full py-2 rounded-lg font-medium text-sm" ,
434+ isDark ? "bg-white text-black hover:bg-white/90" : "bg-black text-white hover:bg-black/90"
435+ ) }
436+ >
437+ Done
438+ </ button >
439+ </ div >
440+ </ div >
441+ ) }
340442 </ div >
341443 ) ;
342444}
0 commit comments