@@ -68,11 +68,13 @@ import {
6868} from "~/components/runs/v3/TaskTriggerSource" ;
6969import { useEnvironment } from "~/hooks/useEnvironment" ;
7070import { useEventSource } from "~/hooks/useEventSource" ;
71+ import { useFeatureFlags } from "~/hooks/useFeatureFlags" ;
7172import { useFuzzyFilter } from "~/hooks/useFuzzyFilter" ;
72- import { useOrganization } from "~/hooks/useOrganizations" ;
73+ import { useIsImpersonating , useOrganization } from "~/hooks/useOrganizations" ;
7374import { useProject } from "~/hooks/useProject" ;
7475import { useSearchParams } from "~/hooks/useSearchParam" ;
7576import { useShortcutKeys } from "~/hooks/useShortcutKeys" ;
77+ import { useOptionalUser } from "~/hooks/useUser" ;
7678import { prisma } from "~/db.server" ;
7779import { findProjectBySlug } from "~/models/project.server" ;
7880import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server" ;
@@ -213,6 +215,15 @@ const TASK_TYPE_SEGMENTS: {
213215 { value : "WEBHOOK" , tooltip : "Webhook tasks" , source : "WEBHOOK" } ,
214216] ;
215217
218+ /** Mirrors the side menu's Webhooks gate, so the two surfaces can't disagree
219+ * about whether this org has webhooks. */
220+ function useHasWebhooksAccess ( ) {
221+ const user = useOptionalUser ( ) ;
222+ const isImpersonating = useIsImpersonating ( ) ;
223+ const featureFlags = useFeatureFlags ( ) ;
224+ return Boolean ( user ?. admin || isImpersonating || featureFlags . hasWebhooksAccess ) ;
225+ }
226+
216227const PAGE_SIZE = 25 ;
217228const TASK_FILTER_KEYS = [ "slug" , "filePath" , "triggerSource" ] ;
218229
@@ -570,6 +581,10 @@ function RunningCell({ state }: { state: UnifiedRunningState | undefined }) {
570581
571582function TaskTypeFilter ( ) {
572583 const { values, replace } = useSearchParams ( ) ;
584+ const hasWebhooksAccess = useHasWebhooksAccess ( ) ;
585+ const segments = hasWebhooksAccess
586+ ? TASK_TYPE_SEGMENTS
587+ : TASK_TYPE_SEGMENTS . filter ( ( option ) => option . value !== "WEBHOOK" ) ;
573588 const raw = parseTypesParam ( values ( "types" ) ) ;
574589 // Single-select: exactly one kind selects it, anything else falls back to All.
575590 const current : TaskTypeSegment = raw . length === 1 ? raw [ 0 ] : ALL_TASK_TYPES ;
@@ -581,7 +596,7 @@ function TaskTypeFilter() {
581596
582597 return (
583598 < >
584- { TASK_TYPE_SEGMENTS . map ( ( option , index ) => (
599+ { segments . map ( ( option , index ) => (
585600 < TaskTypeShortcut
586601 key = { option . value }
587602 shortcut = { String ( index ) }
@@ -593,7 +608,7 @@ function TaskTypeFilter() {
593608 value = { current }
594609 variant = "secondary/small"
595610 onChange = { select }
596- options = { TASK_TYPE_SEGMENTS . map ( ( option , index ) => ( {
611+ options = { segments . map ( ( option , index ) => ( {
597612 value : option . value ,
598613 label : < TaskTypeSegmentLabel option = { option } shortcut = { String ( index ) } /> ,
599614 } ) ) }
@@ -766,6 +781,8 @@ function FailedToLoadStats() {
766781}
767782
768783function TaskTypeBreakdown ( ) {
784+ const hasWebhooksAccess = useHasWebhooksAccess ( ) ;
785+
769786 return (
770787 < div className = "flex flex-col gap-2.5" >
771788 < div >
@@ -799,16 +816,18 @@ function TaskTypeBreakdown() {
799816 you need.
800817 </ Paragraph >
801818 </ div >
802- < div >
803- < div className = "flex items-center gap-1.5" >
804- < WebhookIcon className = "size-4.5 shrink-0 text-webhooks" />
805- < Paragraph variant = "small/bright" > Webhook task</ Paragraph >
819+ { hasWebhooksAccess && (
820+ < div >
821+ < div className = "flex items-center gap-1.5" >
822+ < WebhookIcon className = "size-4.5 shrink-0 text-webhooks" />
823+ < Paragraph variant = "small/bright" > Webhook task</ Paragraph >
824+ </ div >
825+ < Paragraph variant = "small" className = "mt-1" >
826+ Runs from a verified inbound event sent to a hosted endpoint. Each delivery is logged,
827+ and a successful one starts a run.
828+ </ Paragraph >
806829 </ div >
807- < Paragraph variant = "small" className = "mt-1" >
808- Runs from a verified inbound event sent to a hosted endpoint. Each delivery is logged, and
809- a successful one starts a run.
810- </ Paragraph >
811- </ div >
830+ ) }
812831 </ div >
813832 ) ;
814833}
0 commit comments