3232 select( v-model ="filter_client" )
3333 option( :value ='null' ) All
3434 option( v-for ="client in clients" , :value ="client" ) {{ client }}
35+ tr
36+ th.pt-2.pr-3
37+ label AFK:
38+ td
39+ b-form-checkbox( v-model ="filter_afk" size ="sm" switch )
40+ | Filter AFK
3541 div.d-inline-block.border.rounded.p-2.mr-2 ( v-if ="num_events !== 0" )
3642 | Events shown: {{ num_events }}
3743 b-alert.d-inline-block.p-2.mb-0.mt-2 ( v-if ="num_events === 0" , variant ="warning" , show )
6975
7076<script lang="ts">
7177import _ from ' lodash' ;
78+ import { mapState } from ' pinia' ;
7279import { useSettingsStore } from ' ~/stores/settings' ;
7380import { useBucketsStore } from ' ~/stores/buckets' ;
81+ import { getClient } from ' ~/util/awclient' ;
82+ import { canonicalEvents } from ' ~/queries' ;
7483import { seconds_to_duration } from ' ~/util/time' ;
7584
7685export default {
@@ -86,11 +95,13 @@ export default {
8695 filter_hostname: null ,
8796 filter_client: null ,
8897 filter_duration: null ,
98+ filter_afk: false ,
8999 swimlane: null ,
90100 updateTimelineWindow: true ,
91101 };
92102 },
93103 computed: {
104+ ... mapState (useSettingsStore , [' always_active_pattern' ]),
94105 timeintervalDefaultDuration() {
95106 const settingsStore = useSettingsStore ();
96107 return Number (settingsStore .durationDefault );
@@ -110,6 +121,9 @@ export default {
110121 if (this .filter_duration > 0 ) {
111122 desc .push (seconds_to_duration (this .filter_duration ));
112123 }
124+ if (this .filter_afk ) {
125+ desc .push (' AFK filtered' );
126+ }
113127
114128 if (desc .length > 0 ) {
115129 return desc .join (' , ' );
@@ -134,6 +148,10 @@ export default {
134148 this .updateTimelineWindow = false ;
135149 this .getBuckets ();
136150 },
151+ filter_afk() {
152+ this .updateTimelineWindow = false ;
153+ this .getBuckets ();
154+ },
137155 swimlane() {
138156 this .updateTimelineWindow = false ;
139157 this .getBuckets ();
@@ -171,8 +189,74 @@ export default {
171189 }
172190 }
173191
192+ // AFK filtering: use query engine to filter window events by AFK status
193+ if (this .filter_afk ) {
194+ buckets = await this ._applyAfkFilter (buckets );
195+ }
196+
174197 this .buckets = buckets ;
175198 },
199+
200+ // Replaces raw window bucket events with AFK-filtered events via aw query engine.
201+ // Also hides AFK status buckets since they're used for filtering, not display.
202+ _applyAfkFilter : async function (buckets ) {
203+ const bucketsStore = useBucketsStore ();
204+ const result = [];
205+
206+ for (const bucket of buckets ) {
207+ // Hide AFK status buckets when AFK filtering is active
208+ if (bucket .type === ' afkstatus' ) {
209+ continue ;
210+ }
211+
212+ // For window buckets, replace events with AFK-filtered query results
213+ if (bucket .type === ' currentwindow' && bucket .hostname ) {
214+ const afkBucketIds = bucketsStore .bucketsAFK (bucket .hostname );
215+ if (afkBucketIds .length > 0 ) {
216+ try {
217+ const filteredEvents = await this ._queryAfkFilteredEvents (bucket .id , afkBucketIds [0 ]);
218+ // Create a copy with filtered events to avoid mutating frozen all_buckets
219+ result .push ({ ... bucket , events: filteredEvents });
220+ continue ;
221+ } catch (e ) {
222+ console .warn (' AFK filter query failed, falling back to raw events:' , e );
223+ }
224+ }
225+ }
226+
227+ // Keep other buckets unchanged
228+ result .push (bucket );
229+ }
230+
231+ return result ;
232+ },
233+
234+ // Runs a canonicalEvents query to get window events filtered by AFK status,
235+ // respecting the user's always_active_pattern setting.
236+ _queryAfkFilteredEvents : async function (windowBucketId , afkBucketId ) {
237+ const queryCode =
238+ canonicalEvents ({
239+ bid_window: windowBucketId ,
240+ bid_afk: afkBucketId ,
241+ filter_afk: true ,
242+ always_active_pattern: this .always_active_pattern || undefined ,
243+ categories: [],
244+ filter_categories: null ,
245+ }) + ' \n RETURN = events;' ;
246+
247+ const queryArray = queryCode
248+ .split (' ;' )
249+ .map (s => s .trim ())
250+ .filter (s => s )
251+ .map (s => s + ' ;' );
252+
253+ const start = this .daterange [0 ].format ();
254+ const end = this .daterange [1 ].format ();
255+ const timeperiods = [` ${start }/${end } ` ];
256+
257+ const data = await getClient ().query (timeperiods , queryArray );
258+ return data [0 ] || [];
259+ },
176260 },
177261};
178262 </script >
0 commit comments