@@ -6,9 +6,11 @@ import {
66 ChevronUpIcon ,
77 LightBulbIcon ,
88 UserPlusIcon ,
9+ VideoCameraIcon ,
910} from "@heroicons/react/20/solid" ;
11+ import { json } from "@remix-run/node" ;
1012import { Link , useRevalidator , useSubmit } from "@remix-run/react" ;
11- import { LoaderFunctionArgs , ActionFunctionArgs } from "@remix-run/server-runtime" ;
13+ import { ActionFunctionArgs , LoaderFunctionArgs } from "@remix-run/server-runtime" ;
1214import { DiscordIcon } from "@trigger.dev/companyicons" ;
1315import { formatDurationMilliseconds } from "@trigger.dev/core/v3" ;
1416import { TaskRunStatus } from "@trigger.dev/database" ;
@@ -28,6 +30,7 @@ import { AnimatingArrow } from "~/components/primitives/AnimatingArrow";
2830import { Button , LinkButton } from "~/components/primitives/Buttons" ;
2931import { Callout } from "~/components/primitives/Callout" ;
3032import { formatDateTime } from "~/components/primitives/DateTime" ;
33+ import { Dialog , DialogContent , DialogHeader , DialogTitle } from "~/components/primitives/Dialog" ;
3134import { Header1 , Header2 , Header3 } from "~/components/primitives/Headers" ;
3235import { Input } from "~/components/primitives/Input" ;
3336import { NavBar , PageAccessories , PageTitle } from "~/components/primitives/PageHeader" ;
@@ -51,13 +54,7 @@ import {
5154 TableHeaderCell ,
5255 TableRow ,
5356} from "~/components/primitives/Table" ;
54- import {
55- Tooltip as TooltipPrimitive ,
56- SimpleTooltip ,
57- TooltipContent ,
58- TooltipProvider ,
59- TooltipTrigger ,
60- } from "~/components/primitives/Tooltip" ;
57+ import { SimpleTooltip } from "~/components/primitives/Tooltip" ;
6158import TooltipPortal from "~/components/primitives/TooltipPortal" ;
6259import { TaskFunctionName } from "~/components/runs/v3/TaskPath" ;
6360import { TaskRunStatusCombo } from "~/components/runs/v3/TaskRunStatus" ;
@@ -70,6 +67,11 @@ import { useOrganization } from "~/hooks/useOrganizations";
7067import { useProject } from "~/hooks/useProject" ;
7168import { useTextFilter } from "~/hooks/useTextFilter" ;
7269import { Task , TaskActivity , TaskListPresenter } from "~/presenters/v3/TaskListPresenter.server" ;
70+ import {
71+ getUsefulLinksPreference ,
72+ setUsefulLinksPreference ,
73+ uiPreferencesStorage ,
74+ } from "~/services/preferences/uiPreferences.server" ;
7375import { requireUserId } from "~/services/session.server" ;
7476import { cn } from "~/utils/cn" ;
7577import {
@@ -81,12 +83,6 @@ import {
8183 v3TestPath ,
8284 v3TestTaskPath ,
8385} from "~/utils/pathBuilder" ;
84- import {
85- getUsefulLinksPreference ,
86- setUsefulLinksPreference ,
87- uiPreferencesStorage ,
88- } from "~/services/preferences/uiPreferences.server" ;
89- import { json } from "@remix-run/node" ;
9086
9187export const loader = async ( { request, params } : LoaderFunctionArgs ) => {
9288 const userId = await requireUserId ( request ) ;
@@ -624,13 +620,14 @@ const CustomTooltip = ({ active, payload, label }: TooltipProps<number, string>)
624620function HelpfulInfoHasTasks ( { onClose } : { onClose : ( ) => void } ) {
625621 const organization = useOrganization ( ) ;
626622 const project = useProject ( ) ;
623+ const [ isVideoDialogOpen , setIsVideoDialogOpen ] = useState ( false ) ;
627624
628625 return (
629626 < div className = "grid h-full max-h-full grid-rows-[auto_1fr] overflow-hidden bg-background-bright" >
630627 < div className = "overflow-y-scroll p-3 pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" >
631628 < div className = "mb-2 flex items-center justify-between gap-2 border-b border-grid-dimmed pb-2" >
632629 < Header2 className = "flex items-center gap-2" >
633- < LightBulbIcon className = "size-4 text-sun-500" />
630+ < LightBulbIcon className = "size-4 min-w-4 text-sun-500" />
634631 Helpful next steps
635632 </ Header2 >
636633 < Button
@@ -643,16 +640,37 @@ function HelpfulInfoHasTasks({ onClose }: { onClose: () => void }) {
643640 />
644641 </ div >
645642 < LinkWithIcon
643+ variant = "withIcon"
646644 to = { v3TestPath ( organization , project ) }
647645 description = "Test your tasks"
648646 icon = { < BeakerIcon className = "size-5 text-lime-500" /> }
649647 />
650648 < LinkWithIcon
649+ variant = "withIcon"
651650 to = { inviteTeamMemberPath ( organization ) }
652651 description = "Invite team members"
653652 icon = { < UserPlusIcon className = "size-5 text-amber-500" /> }
654653 />
654+ < div
655+ role = "button"
656+ onClick = { ( ) => setIsVideoDialogOpen ( true ) }
657+ className = { cn (
658+ "group flex w-full items-center justify-between gap-2 rounded-md p-1 pr-3 transition hover:bg-charcoal-750" ,
659+ variants [ "withIcon" ] . container
660+ ) }
661+ >
662+ < div className = "flex items-center gap-2" >
663+ < div className = { variants [ "withIcon" ] . iconContainer } >
664+ < VideoCameraIcon className = "size-5 text-rose-500" />
665+ </ div >
666+ < Paragraph variant = "base" className = "transition-colors group-hover:text-text-bright" >
667+ Watch a 14 min walkthrough video
668+ </ Paragraph >
669+ </ div >
670+ < AnimatingArrow direction = "right" theme = "dimmed" />
671+ </ div >
655672 < LinkWithIcon
673+ variant = "withIcon"
656674 to = "https://trigger.dev/discord"
657675 description = "Join our Discord for help and support"
658676 icon = { < DiscordIcon className = "size-5" /> }
@@ -667,31 +685,18 @@ function HelpfulInfoHasTasks({ onClose }: { onClose: () => void }) {
667685 < LinkWithIcon
668686 to = { docsPath ( "/writing-tasks-introduction" ) }
669687 description = "How to write a task"
670- icon = { < BookOpenIcon className = "size-5 text-text-dimmed" /> }
671688 isExternal
672689 />
673690 < LinkWithIcon
674691 to = { docsPath ( "/tasks/scheduled" ) }
675692 description = "Scheduled tasks (cron)"
676- icon = { < BookOpenIcon className = "size-5 text-text-dimmed" /> }
677- isExternal
678- />
679- < LinkWithIcon
680- to = { docsPath ( "/triggering" ) }
681- description = "How to trigger a task"
682- icon = { < BookOpenIcon className = "size-5 text-text-dimmed" /> }
683- isExternal
684- />
685- < LinkWithIcon
686- to = { docsPath ( "/cli-dev" ) }
687- description = "Running the CLI"
688- icon = { < BookOpenIcon className = "size-5 text-text-dimmed" /> }
689693 isExternal
690694 />
695+ < LinkWithIcon to = { docsPath ( "/triggering" ) } description = "How to trigger a task" isExternal />
696+ < LinkWithIcon to = { docsPath ( "/cli-dev" ) } description = "Running the CLI" isExternal />
691697 < LinkWithIcon
692698 to = { docsPath ( "/how-it-works" ) }
693699 description = "How Trigger.dev works"
694- icon = { < BookOpenIcon className = "size-5 text-text-dimmed" /> }
695700 isExternal
696701 />
697702 < div className = "mb-2 flex items-center gap-2 border-b border-grid-dimmed pb-2 pt-6" >
@@ -703,145 +708,157 @@ function HelpfulInfoHasTasks({ onClose }: { onClose: () => void }) {
703708 < LinkWithIcon
704709 to = { docsPath ( "/examples/dall-e3-generate-image" ) }
705710 description = "DALL·E 3 image generation"
706- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
707711 isExternal
708712 />
709713 < LinkWithIcon
710714 to = { docsPath ( "/examples/deepgram-transcribe-audio" ) }
711715 description = "Deepgram audio transcription"
712- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
713716 isExternal
714717 />
715718 < LinkWithIcon
716719 to = { docsPath ( "/examples/fal-ai-image-to-cartoon" ) }
717720 description = "Fal.ai image to cartoon"
718- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
719721 isExternal
720722 />
721723 < LinkWithIcon
722724 to = { docsPath ( "/examples/fal-ai-realtime" ) }
723725 description = "Fal.ai with Realtime"
724- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
725726 isExternal
726727 />
727728 < LinkWithIcon
728729 to = { docsPath ( "/examples/ffmpeg-video-processing" ) }
729730 description = "FFmpeg video processing"
730- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
731731 isExternal
732732 />
733733 < LinkWithIcon
734734 to = { docsPath ( "/examples/firecrawl-url-crawl" ) }
735735 description = "Firecrawl URL crawl"
736- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
737736 isExternal
738737 />
739738 < LinkWithIcon
740739 to = { docsPath ( "/examples/libreoffice-pdf-conversion" ) }
741740 description = "LibreOffice PDF conversion"
742- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
743741 isExternal
744742 />
745743 < LinkWithIcon
746744 to = { docsPath ( "/examples/open-ai-with-retrying" ) }
747745 description = "OpenAI with retrying"
748- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
749746 isExternal
750747 />
751748 < LinkWithIcon
752749 to = { docsPath ( "/examples/pdf-to-image" ) }
753750 description = "PDF to image"
754- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
755- isExternal
756- />
757- < LinkWithIcon
758- to = { docsPath ( "/examples/puppeteer" ) }
759- description = "Puppeteer"
760- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
761- isExternal
762- />
763- < LinkWithIcon
764- to = { docsPath ( "/examples/react-pdf" ) }
765- description = "React to PDF"
766- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
767751 isExternal
768752 />
753+ < LinkWithIcon to = { docsPath ( "/examples/puppeteer" ) } description = "Puppeteer" isExternal />
754+ < LinkWithIcon to = { docsPath ( "/examples/react-pdf" ) } description = "React to PDF" isExternal />
769755 < LinkWithIcon
770756 to = { docsPath ( "/examples/resend-email-sequence" ) }
771757 description = "Resend email sequence"
772- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
773758 isExternal
774759 />
775760 < LinkWithIcon
776761 to = { docsPath ( "/examples/scrape-hacker-news" ) }
777762 description = "Scrape Hacker News"
778- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
779763 isExternal
780764 />
781765 < LinkWithIcon
782766 to = { docsPath ( "/examples/sentry-error-tracking" ) }
783767 description = "Sentry error tracking"
784- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
785768 isExternal
786769 />
787770 < LinkWithIcon
788771 to = { docsPath ( "/examples/sharp-image-processing" ) }
789772 description = "Sharp image processing"
790- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
791773 isExternal
792774 />
793775 < LinkWithIcon
794776 to = { docsPath ( "/examples/supabase-database-operations" ) }
795777 description = "Supabase database operations"
796- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
797778 isExternal
798779 />
799780 < LinkWithIcon
800781 to = { docsPath ( "/examples/supabase-storage-upload" ) }
801782 description = "Supabase Storage upload"
802- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
803783 isExternal
804784 />
805785 < LinkWithIcon
806786 to = { docsPath ( "/examples/vercel-ai-sdk" ) }
807787 description = "Vercel AI SDK"
808- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
809788 isExternal
810789 />
811790 < LinkWithIcon
812791 to = { docsPath ( "/examples/vercel-sync-env-vars" ) }
813792 description = "Vercel sync environment variables"
814- icon = { < TaskIcon className = "size-4 text-text-dimmed" /> }
815793 isExternal
816794 />
817795 </ div >
796+ < Dialog open = { isVideoDialogOpen } onOpenChange = { setIsVideoDialogOpen } >
797+ < DialogContent className = "sm:max-w-screen-lg" >
798+ < DialogHeader className = "mb-4 pt-1" >
799+ < DialogTitle > Trigger.dev walkthrough</ DialogTitle >
800+ </ DialogHeader >
801+ < div className = "aspect-video" >
802+ < iframe
803+ width = "100%"
804+ height = "100%"
805+ src = "https://www.youtube.com/embed/YH_4c0K7fGM?si=BcX6MAt_V139sRw9"
806+ title = "Trigger.dev walkthrough"
807+ allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
808+ referrerPolicy = "strict-origin-when-cross-origin"
809+ allowFullScreen
810+ />
811+ </ div >
812+ </ DialogContent >
813+ </ Dialog >
818814 </ div >
819815 ) ;
820816}
821817
818+ const variants = {
819+ withIcon : {
820+ container : "" ,
821+ iconContainer :
822+ "grid size-9 min-w-9 place-items-center rounded border border-transparent bg-charcoal-750 shadow transition group-hover:border-charcoal-650" ,
823+ } ,
824+ minimal : {
825+ container : "pl-3 py-2" ,
826+ iconContainer : "" ,
827+ } ,
828+ } as const ;
829+
830+ type LinkWithIconProps = {
831+ to : string ;
832+ description : string ;
833+ icon ?: React . ReactNode ;
834+ isExternal ?: boolean ;
835+ variant ?: keyof typeof variants ;
836+ } ;
837+
822838function LinkWithIcon ( {
823839 to,
824840 description,
825841 icon,
826842 isExternal,
827- } : {
828- to : string ;
829- description : string ;
830- icon : React . ReactNode ;
831- isExternal ?: boolean ;
832- } ) {
843+ variant = "minimal" ,
844+ } : LinkWithIconProps ) {
845+ const variation = variants [ variant ] ;
846+
833847 return (
834848 < Link
835849 to = { to }
836850 target = { isExternal ? "_blank" : undefined }
837851 rel = { isExternal ? "noreferrer" : undefined }
838- className = "group flex w-full items-center justify-between gap-2 rounded-md p-1 pr-3 transition hover:bg-charcoal-750"
852+ className = { cn (
853+ "group flex w-full items-center justify-between gap-2 rounded-md p-1 pr-3 transition hover:bg-charcoal-750" ,
854+ variation . container
855+ ) }
839856 >
840857 < div className = "flex items-center gap-2" >
841- < div className = "grid size-9 min-w-9 place-items-center rounded border border-transparent bg-charcoal-750 shadow transition group-hover:border-charcoal-650" >
842- { icon }
843- </ div >
844- < Paragraph variant = "base" > { description } < /Paragraph >
858+ { variant === "withIcon" && icon && < div className = { variation . iconContainer } > { icon } </ div > }
859+ < Paragraph variant = "base" className = "transition-colors group-hover:text-text-bright" >
860+ { description }
861+ </ Paragraph >
845862 </ div >
846863 < AnimatingArrow direction = { isExternal ? "topRight" : "right" } theme = "dimmed" />
847864 </ Link >
0 commit comments