@@ -10,6 +10,7 @@ import {
1010 computeFleetSummary ,
1111 computeScoutRollups ,
1212 getScoutOrigin ,
13+ isScoutCreatedByUser ,
1314 sortConfigsForDisplay ,
1415} from "@posthog/core/scouts/scoutPresentation" ;
1516import {
@@ -27,10 +28,12 @@ import { RelativeTimestamp } from "@posthog/ui/primitives/RelativeTimestamp";
2728import { track } from "@posthog/ui/shell/analytics" ;
2829import { Box , Flex , Text } from "@radix-ui/themes" ;
2930import { useEffect , useMemo , useRef , useState } from "react" ;
31+ import { useMeQuery } from "../../auth/useMeQuery" ;
3032import { useScoutChatTask } from "../hooks/useScoutChatTask" ;
3133import { useScoutConfigMutations } from "../hooks/useScoutConfigMutations" ;
3234import { useScoutConfigs } from "../hooks/useScoutConfigs" ;
3335import { useScoutRuns } from "../hooks/useScoutRuns" ;
36+ import { useScoutSkillCreators } from "../hooks/useScoutSkillCreators" ;
3437import { FleetFindingsCallout } from "./FleetFindingsCallout" ;
3538import { FleetMemoryCallout } from "./FleetMemoryCallout" ;
3639import { ScoutAlphaBanner } from "./ScoutAlphaBanner" ;
@@ -154,6 +157,9 @@ function ScoutsFleetList({ configs }: { configs: ScoutConfig[] }) {
154157 const { data : runsWindow } = useScoutRuns ( ) ;
155158 const { updateConfig } = useScoutConfigMutations ( ) ;
156159 const [ hideDisabled , setHideDisabled ] = useState ( false ) ;
160+ const [ createdByMe , setCreatedByMe ] = useState ( false ) ;
161+ const { data : creators } = useScoutSkillCreators ( ) ;
162+ const { data : currentUser } = useMeQuery ( ) ;
157163 useTrackFleetViewed ( configs ) ;
158164
159165 const runs = runsWindow ?. runs ;
@@ -162,10 +168,28 @@ function ScoutsFleetList({ configs }: { configs: ScoutConfig[] }) {
162168 ( ) => computeFleetSummary ( configs , rollups ) ,
163169 [ configs , rollups ] ,
164170 ) ;
171+ // Null creators = the skills API is gated for this org, so authorship is
172+ // unknowable; skip the affordance instead of offering an always-empty filter.
173+ const canFilterByCreator = ! ! creators && ! ! currentUser ;
165174 const visibleConfigs = useMemo ( ( ) => {
166- const sorted = sortConfigsForDisplay ( configs ) ;
167- return hideDisabled ? sorted . filter ( ( config ) => config . enabled ) : sorted ;
168- } , [ configs , hideDisabled ] ) ;
175+ let sorted = sortConfigsForDisplay ( configs ) ;
176+ if ( hideDisabled ) {
177+ sorted = sorted . filter ( ( config ) => config . enabled ) ;
178+ }
179+ if ( createdByMe && canFilterByCreator ) {
180+ sorted = sorted . filter ( ( config ) =>
181+ isScoutCreatedByUser ( creators ?. get ( config . skill_name ) , currentUser ) ,
182+ ) ;
183+ }
184+ return sorted ;
185+ } , [
186+ configs ,
187+ hideDisabled ,
188+ createdByMe ,
189+ canFilterByCreator ,
190+ creators ,
191+ currentUser ,
192+ ] ) ;
169193
170194 return (
171195 < Flex direction = "column" gap = "3" >
@@ -187,6 +211,36 @@ function ScoutsFleetList({ configs }: { configs: ScoutConfig[] }) {
187211 </ span >
188212 </ Text >
189213 < span className = "flex-1" />
214+ { canFilterByCreator ? (
215+ < button
216+ type = "button"
217+ aria-pressed = { createdByMe }
218+ onClick = { ( ) => {
219+ const next = ! createdByMe ;
220+ setCreatedByMe ( next ) ;
221+ track ( ANALYTICS_EVENTS . SCOUT_ACTION , {
222+ action_type : "toggle_created_by_me" ,
223+ surface : "fleet_list" ,
224+ created_by_me : next ,
225+ filter_match_count : next
226+ ? configs . filter ( ( config ) =>
227+ isScoutCreatedByUser (
228+ creators ?. get ( config . skill_name ) ,
229+ currentUser ,
230+ ) ,
231+ ) . length
232+ : undefined ,
233+ } ) ;
234+ } }
235+ className = { `rounded px-1.5 py-0.5 text-[12px] transition-colors ${
236+ createdByMe
237+ ? "bg-(--gray-4) text-gray-12"
238+ : "text-gray-10 hover:bg-gray-3 hover:text-gray-12"
239+ } `}
240+ >
241+ Created by me
242+ </ button >
243+ ) : null }
190244 < button
191245 type = "button"
192246 onClick = { ( ) => {
@@ -238,14 +292,22 @@ function ScoutsFleetList({ configs }: { configs: ScoutConfig[] }) {
238292 { /* Bounded to roughly 10 rows; larger fleets scroll within the section. */ }
239293 < div className = "max-h-[710px] overflow-y-auto" >
240294 < Flex direction = "column" gap = "2" >
241- { visibleConfigs . map ( ( config ) => (
242- < ScoutRowCard
243- key = { config . id }
244- config = { config }
245- rollup = { rollups . get ( config . skill_name ) }
246- onUpdate = { updateConfig }
247- />
248- ) ) }
295+ { visibleConfigs . length === 0 ? (
296+ < Text className = "px-1 py-2 text-[12.5px] text-gray-10" >
297+ { createdByMe
298+ ? "No scouts created by you match the current filters."
299+ : "No scouts match the current filters." }
300+ </ Text >
301+ ) : (
302+ visibleConfigs . map ( ( config ) => (
303+ < ScoutRowCard
304+ key = { config . id }
305+ config = { config }
306+ rollup = { rollups . get ( config . skill_name ) }
307+ onUpdate = { updateConfig }
308+ />
309+ ) )
310+ ) }
249311 </ Flex >
250312 </ div >
251313
0 commit comments