@@ -9,12 +9,20 @@ import {
99 Globe ,
1010 Linkedin ,
1111 Mail ,
12+ Share2 ,
1213 Star ,
1314 User ,
1415} from 'lucide-react'
1516import { useParams , useRouter , useSearchParams } from 'next/navigation'
1617import ReactMarkdown from 'react-markdown'
17- import { Button } from '@/components/emcn'
18+ import {
19+ Button ,
20+ Copy ,
21+ Popover ,
22+ PopoverContent ,
23+ PopoverItem ,
24+ PopoverTrigger ,
25+ } from '@/components/emcn'
1826import {
1927 DropdownMenu ,
2028 DropdownMenuContent ,
@@ -23,6 +31,7 @@ import {
2331} from '@/components/ui/dropdown-menu'
2432import { useSession } from '@/lib/auth-client'
2533import { createLogger } from '@/lib/logs/console/logger'
34+ import { getBaseUrl } from '@/lib/urls/utils'
2635import { cn } from '@/lib/utils'
2736import type { CredentialRequirement } from '@/lib/workflows/credential-extractor'
2837import type { Template } from '@/app/templates/templates'
@@ -63,7 +72,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
6372 > ( [ ] )
6473 const [ isLoadingWorkspaces , setIsLoadingWorkspaces ] = useState ( false )
6574 const [ showWorkspaceSelectorForEdit , setShowWorkspaceSelectorForEdit ] = useState ( false )
66- const [ showWorkspaceSelectorForUse , setShowWorkspaceSelectorForUse ] = useState ( false )
75+ const [ sharePopoverOpen , setSharePopoverOpen ] = useState ( false )
6776
6877 const currentUserId = session ?. user ?. id || null
6978
@@ -351,8 +360,6 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
351360 // In workspace context, use current workspace directly
352361 if ( isWorkspaceContext && workspaceId ) {
353362 handleWorkspaceSelectForUse ( workspaceId )
354- } else {
355- setShowWorkspaceSelectorForUse ( true )
356363 }
357364 }
358365
@@ -415,7 +422,6 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
415422 if ( isUsing || ! template ) return
416423
417424 setIsUsing ( true )
418- setShowWorkspaceSelectorForUse ( false )
419425 try {
420426 const response = await fetch ( `/api/templates/${ template . id } /use` , {
421427 method : 'POST' ,
@@ -518,6 +524,57 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
518524 }
519525 }
520526
527+ /**
528+ * Shares the template to X (Twitter)
529+ */
530+ const handleShareToTwitter = ( ) => {
531+ if ( ! template ) return
532+
533+ setSharePopoverOpen ( false )
534+ const templateUrl = `${ getBaseUrl ( ) } /templates/${ template . id } `
535+
536+ let tweetText = `🚀 Check out this workflow template: ${ template . name } `
537+
538+ if ( template . details ?. tagline ) {
539+ const taglinePreview =
540+ template . details . tagline . length > 100
541+ ? `${ template . details . tagline . substring ( 0 , 100 ) } ...`
542+ : template . details . tagline
543+ tweetText += `\n\n${ taglinePreview } `
544+ }
545+
546+ const maxTextLength = 280 - 23 - 1
547+ if ( tweetText . length > maxTextLength ) {
548+ tweetText = `${ tweetText . substring ( 0 , maxTextLength - 3 ) } ...`
549+ }
550+
551+ const twitterUrl = `https://twitter.com/intent/tweet?text=${ encodeURIComponent ( tweetText ) } &url=${ encodeURIComponent ( templateUrl ) } `
552+ window . open ( twitterUrl , '_blank' , 'noopener,noreferrer' )
553+ }
554+
555+ /**
556+ * Shares the template to LinkedIn.
557+ */
558+ const handleShareToLinkedIn = ( ) => {
559+ if ( ! template ) return
560+
561+ setSharePopoverOpen ( false )
562+ const templateUrl = `${ getBaseUrl ( ) } /templates/${ template . id } `
563+ const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${ encodeURIComponent ( templateUrl ) } `
564+ window . open ( linkedInUrl , '_blank' , 'noopener,noreferrer' )
565+ }
566+
567+ const handleCopyLink = async ( ) => {
568+ setSharePopoverOpen ( false )
569+ const templateUrl = `${ getBaseUrl ( ) } /templates/${ template ?. id } `
570+ try {
571+ await navigator . clipboard . writeText ( templateUrl )
572+ logger . info ( 'Template link copied to clipboard' )
573+ } catch ( error ) {
574+ logger . error ( 'Failed to copy link:' , error )
575+ }
576+ }
577+
521578 return (
522579 < div className = { cn ( 'flex min-h-screen flex-col' , isWorkspaceContext && 'pl-64' ) } >
523580 < div className = 'flex flex-1 overflow-hidden' >
@@ -530,7 +587,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
530587 className = 'flex items-center gap-[6px] font-medium text-[#ADADAD] text-[14px] transition-colors hover:text-white'
531588 >
532589 < ArrowLeft className = 'h-[14px] w-[14px]' />
533- < span > Back </ span >
590+ < span > More Templates </ span >
534591 </ button >
535592 </ div >
536593
@@ -622,7 +679,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
622679 < >
623680 { ! currentUserId ? (
624681 < Button
625- variant = 'active '
682+ variant = 'primary '
626683 onClick = { ( ) => {
627684 const callbackUrl =
628685 isWorkspaceContext && workspaceId
@@ -645,48 +702,39 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
645702 >
646703 { isUsing ? 'Creating...' : 'Use template' }
647704 </ Button >
648- ) : (
649- < DropdownMenu
650- open = { showWorkspaceSelectorForUse }
651- onOpenChange = { setShowWorkspaceSelectorForUse }
652- >
653- < DropdownMenuTrigger asChild >
654- < Button
655- variant = 'primary'
656- onClick = { ( ) => setShowWorkspaceSelectorForUse ( true ) }
657- disabled = { isUsing || isLoadingWorkspaces }
658- className = 'h-[32px] rounded-[6px] px-[16px] text-[#FFFFFF] text-[14px]'
659- >
660- { isUsing ? 'Creating...' : isLoadingWorkspaces ? 'Loading...' : 'Use' }
661- < ChevronDown className = 'ml-2 h-4 w-4' />
662- </ Button >
663- </ DropdownMenuTrigger >
664- < DropdownMenuContent align = 'end' className = 'w-56' >
665- { workspaces . length === 0 ? (
666- < DropdownMenuItem disabled className = 'text-muted-foreground text-sm' >
667- No workspaces with write access
668- </ DropdownMenuItem >
669- ) : (
670- workspaces . map ( ( workspace ) => (
671- < DropdownMenuItem
672- key = { workspace . id }
673- onClick = { ( ) => handleWorkspaceSelectForUse ( workspace . id ) }
674- className = 'flex cursor-pointer items-center justify-between'
675- >
676- < div className = 'flex flex-col' >
677- < span className = 'font-medium text-sm' > { workspace . name } </ span >
678- < span className = 'text-muted-foreground text-xs capitalize' >
679- { workspace . permissions } access
680- </ span >
681- </ div >
682- </ DropdownMenuItem >
683- ) )
684- ) }
685- </ DropdownMenuContent >
686- </ DropdownMenu >
687- ) }
705+ ) : null }
688706 </ >
689707 ) }
708+
709+ { /* Share button */ }
710+ < Popover open = { sharePopoverOpen } onOpenChange = { setSharePopoverOpen } >
711+ < PopoverTrigger asChild >
712+ < Button variant = 'active' className = 'h-[32px] rounded-[6px] px-[12px]' >
713+ < Share2 className = 'h-[14px] w-[14px]' />
714+ </ Button >
715+ </ PopoverTrigger >
716+ < PopoverContent align = 'end' side = 'bottom' sideOffset = { 8 } >
717+ < PopoverItem onClick = { handleCopyLink } >
718+ < Copy className = 'h-3 w-3' />
719+ < span > Copy link</ span >
720+ </ PopoverItem >
721+ < PopoverItem onClick = { handleShareToTwitter } >
722+ < svg
723+ className = 'h-3 w-3'
724+ viewBox = '0 0 24 24'
725+ fill = 'currentColor'
726+ xmlns = 'http://www.w3.org/2000/svg'
727+ >
728+ < path d = 'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z' />
729+ </ svg >
730+ < span > Share on X</ span >
731+ </ PopoverItem >
732+ < PopoverItem onClick = { handleShareToLinkedIn } >
733+ < Linkedin className = 'h-3 w-3' />
734+ < span > Share on LinkedIn</ span >
735+ </ PopoverItem >
736+ </ PopoverContent >
737+ </ Popover >
690738 </ div >
691739 </ div >
692740
0 commit comments