11<script  setup lang="ts">
22import  { useRouter  } from  ' vue-router' 
33import  { useEvents  } from  ' @/composables/events' 
4- import  { type   PropType ,  onUnmounted ,  computed  } from  ' vue' 
4+ import  { onUnmounted ,  computed ,  useTemplateRef ,  onMounted ,  reactive ,  watch ,  ref ,   } from  ' vue' 
55import  { usePrettyDates  } from  ' @/composables/usePrettyDate' 
66import  { usePrettyHttp  } from  ' @/composables/http' 
7+ import  { Modal  } from  ' bootstrap' 
8+ import  { useService  } from  ' @/composables/services' 
79
810const =  defineProps ({
9-     service : { type: Object   as   PropType < HttpService >  }, 
11+     serviceName : { type: String , required:  false }, 
1012    path: { type: String , required: false }, 
1113    method: { type: String , required: false } 
1214}) 
1315
14- const =  [] 
15- if  (props .service ){
16-     labels .push ({ name: ' name' props .service ! . name  }) 
16+ const =  ref < any []>([]) 
17+ if  (props .serviceName ){
18+     labels .value . push ({ name: ' name' props .serviceName  }) 
1719    if  (props .path ){ 
18-         labels .push ({ name: ' path' props .path  }) 
20+         labels .value . push ({ name: ' path' props .path  }) 
1921    } 
2022    if  (props .method ) { 
21-         labels .push ({ name: ' method' props .method  }) 
23+         labels .value . push ({ name: ' method' props .method  }) 
2224    } 
2325} 
2426
2527const =  useRouter ()
2628const =  useEvents ()
27- const =  fetch (' http' ... labels )
29+ const =  useService ()
30+ const events : data, close } =  fetch (' http' ... labels .value )
2831const =  usePrettyDates ()
2932const =  usePrettyHttp ()
33+ const close : closeServices } =  fetchServices (' http' true );
34+ const =  useTemplateRef (' dialogRef' 
35+ let  dialog:  Modal  |  undefined ;
36+ type  CheckboxFilter  =  ' Not' |  ' Single' |  ' Multi' 
37+ interface  Filter  {
38+     service:  FilterItem  
39+     method:  FilterItem  &  { custom? :  string } 
40+ } 
41+ interface  FilterItem  {
42+     checkbox:  boolean  
43+     state:  CheckboxFilter  
44+     value:  string [] 
45+ } 
46+ const =  reactive <Filter >({
47+     service: { state: ' Not' false , value: [] }, 
48+     method: { state: ' Not' false , value: [' GET'  
49+ }) 
50+ 
51+ onMounted (() =>  {
52+     dialog  =  Modal .getOrCreateInstance (dialogRef .value ! ); 
53+     const =  localStorage .getItem (` http-requests-${getFilterCacheKey ()} ` ) 
54+     console .log (' load ' + props .serviceName ) 
55+     console .log (s ) 
56+     if  (s  &&  s  !==  ' '  
57+         const =  JSON .parse (s ) 
58+         Object .assign (filter , saved ) 
59+         console .log (filter ) 
60+     } 
61+ }) 
3062
3163function  goToRequest(event :  ServiceEvent ){
3264    if  (getSelection ()?.toString ()) { 
@@ -42,6 +74,48 @@ function eventData(event: ServiceEvent): HttpEventData{
4274    return  <HttpEventData >event .data  
4375} 
4476
77+ watch (filter , () =>  {
78+     localStorage .setItem (` http-requests-${getFilterCacheKey ()} ` , JSON .stringify (filter )) 
79+ }) 
80+ 
81+ const =  computed <ServiceEvent []>(() =>  {
82+     let  result =  data .value ;; 
83+     switch  (filter .service .state ) { 
84+         case  ' Single'  
85+             result  =  result .filter (x  =>  x .traits .name  ===  filter .service .value [0 ]); 
86+             break ; 
87+         case  ' Multi'  
88+             result  =  result .filter (x  =>  x .traits .name  &&  filter .service .value .includes (x .traits .name )); 
89+     } 
90+     const =  filter .method .custom ?.split ('  '  
91+     switch  (filter .method .state ) { 
92+         case  ' Single'  
93+             if  (filter .method .value [0 ] ===  ' CUSTOM'  
94+                 result  =  result .filter (x  =>  custom ?.includes ((x .data  as  HttpEventData ).request .method )); 
95+             } else  { 
96+                 result  =  result .filter (x  =>  filter .method .value [0 ] ===  (x .data  as  HttpEventData ).request .method ); 
97+             } 
98+             break ; 
99+         case  ' Multi'  
100+             result  =  result .filter (x  =>  { 
101+                 const =  (x .data  as  HttpEventData ).request .method ; 
102+                 for  (const of  filter .method .value ) { 
103+                     if  (m  ===  ' CUSTOM'  
104+                         if  (custom ?.includes (method )) { 
105+                             return  true ; 
106+                         } 
107+                     }else  { 
108+                         if  (m  ===  method ) { 
109+                             return  true ; 
110+                         }  
111+                     } 
112+                 } 
113+                 return  false ; 
114+             }) 
115+     } 
116+     return  result  
117+ }) 
118+ 
45119const =  computed (() =>  {
46120    if  (! events .value ) { 
47121        return  false  
@@ -54,15 +128,120 @@ const hasDeprecatedRequests = computed(() => {
54128    return  false      
55129}) 
56130
131+ const =  computed ({
132+     get : function () { 
133+         if  (filter .service .state  ===  ' Single'  
134+             if  (filter .service .value ?.length  ===  0 ) { 
135+                 return  services .value ?.[0 ]?.name  
136+             } else  { 
137+                 return  filter .service .value [0 ] 
138+             } 
139+         } 
140+         return  filter .service .value  
141+     }, 
142+     set : function (val :  any ) { 
143+         if  (filter .service .state  ===  ' Single'  
144+             if  (! val ) { 
145+                 filter .service .value  =  [] 
146+             } else  { 
147+                 filter .service .value  =  [val ] 
148+             } 
149+         } else  { 
150+            filter .service .value  =  val  
151+         } 
152+     } 
153+ }) 
154+ const =  [' GET' ' POST' ' PUT' ' PATCH' ' DELETE' ' HEAD' ' OPTIONS' ' TRACE' ' QUERY' ' CUSTOM' 
155+ const =  computed ({
156+     get : function () { 
157+         if  (filter .method .state  ===  ' Single'  
158+             if  (filter .method .value ?.length  ===  0 ) { 
159+                 return  methods [0 ] 
160+             } else  { 
161+                 return  filter .method .value [0 ] 
162+             } 
163+         } 
164+         return  filter .method .value  
165+     }, 
166+     set : function (val :  any ) { 
167+         if  (filter .method .state  ===  ' Single'  
168+             if  (! val ) { 
169+                 filter .method .value  =  [] 
170+             } else  { 
171+                 filter .method .value  =  [val ] 
172+             } 
173+         } else  { 
174+            filter .method .value  =  val  
175+         } 
176+     } 
177+ }) 
178+ 
57179onUnmounted (() =>  {
58180    close () 
181+     closeServices () 
182+ }) 
183+ function  showDialog() {
184+     dialog ?.show () 
185+ } 
186+ function  changeCheckbox(fi :  FilterItem ) {
187+     switch  (fi .state ) { 
188+         case  ' Not'  
189+             fi .state  =  ' Single'  
190+             fi .checkbox  =  true  
191+             break ; 
192+         case  ' Single'  
193+             fi .state  =  ' Multi'  
194+             fi .checkbox  =  true  
195+             break ; 
196+         case  ' Multi'  
197+             fi .state  =  ' Not'  
198+             fi .checkbox  =  false  
199+             break ; 
200+     } 
201+ } 
202+ function  getId(s :  string ) {
203+     return  s .replaceAll ('  ' ' -' toLowerCase () 
204+ } 
205+ const =  computed (() =>  {
206+     let  counter =  0 ; 
207+     if  (filter .service .state  !==  ' Not'  
208+         counter ++ ; 
209+     } 
210+     if  (filter .method .state  !==  ' Not'  
211+         counter ++ ; 
212+     } 
213+     return  counter ; 
59214}) 
215+ function  getFilterCacheKey() {
216+     if  (! props .serviceName ) { 
217+         return  ' filter'  
218+     } 
219+     if  (! props .path ) { 
220+         return  ' filter-' +  props .serviceName  
221+     } 
222+     return  ` filter-${props .serviceName }-${props .path }-${props .method } `  
223+ } 
60224script >
61225
62226<template >
63227    <div  class =" card" 
64228        <div  class =" card-body" 
65-             <div  class =" card-title text-center" div >
229+             <div  class =" row justify-content-end mb-1" 
230+                 <div  class =" col-4" 
231+                     <h6  class =" card-title text-center" h6 >
232+                 </div >
233+                 <div  class =" col-4 d-flex justify-content-end" 
234+                     <button  class =" btn btn-outline-primary position-relative" style =" --bs-btn-padding-y : .25rem  ; --bs-btn-padding-x : .5rem  ; --bs-btn-font-size : .75rem  ;" @click =" showDialog" 
235+                     <i  class =" bi bi-funnel" i > Filter
236+ 
237+                     <span  v-if =" activeFiltersCount > 0" 
238+                         class =" position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" 
239+                         {{ activeFiltersCount }}
240+                     </span >
241+                 </button >
242+                 </div >
243+             </div >
244+             
66245            <table  class =" table dataTable selectable" data-testid =" requests" 
67246                <thead >
68247                    <tr >
@@ -95,10 +274,67 @@ onUnmounted(() => {
95274            </table >
96275        </div >
97276    </div >
277+ 
278+     <div  class =" modal fade" tabindex =" -1" aria-hidden =" true" ref =" dialogRef" 
279+         <div  class =" modal-dialog modal-dialog-scrollable" 
280+             <div  class =" modal-content" 
281+                 <div  class =" modal-header" 
282+                     <h6  class =" modal-title" h6 >
283+                     <button  type =" button" class =" btn-close" data-bs-dismiss =" modal" aria-label =" Close" button >
284+                 </div >
285+                 <div  class =" modal-body" 
286+                     <div  class =" row mb-3" 
287+                         <div  class =" col" 
288+                             <div  class =" form-check" 
289+                                 <input  class =" form-check-input" type =" checkbox" v-model =" filter.service.checkbox" id =" service" @change =" changeCheckbox(filter.service)" 
290+                                 <label  class =" form-check-label" for =" service" label >
291+                             </div >
292+                         </div >
293+                         <div  class =" col" 
294+                             <select  class =" form-select form-select-sm" v-model =" service" aria-label =" Service" v-if =" filter.service.state === 'Single'" id =" service-single" 
295+                                 <option  v-for =" service of services" option >
296+                             </select >
297+                             <div  class =" form-check" v-for =" s of services" v-if =" filter.service.state === 'Multi'" 
298+                                 <input  class =" form-check-input" name =" service" :value =" s.name" v-model =" service" type =" checkbox" :id =" getId(s.name)" 
299+                                 <label  class =" form-check-label" :for =" getId(s.name)" label >
300+                             </div >
301+                         </div >
302+                     </div >
303+ 
304+                     <div  class =" row mb-3" 
305+                         <div  class =" col" 
306+                             <div  class =" form-check" 
307+                                 <input  class =" form-check-input" type =" checkbox" v-model =" filter.method.checkbox" id =" method" @change =" changeCheckbox(filter.method)" 
308+                                 <label  class =" form-check-label" for =" method" label >
309+                             </div >
310+                         </div >
311+                         <div  class =" col" 
312+                             <div  class =" row ms-0 me-0" 
313+                                 <select  class =" form-select form-select-sm" v-model =" method" aria-label =" Method" v-if =" filter.method.state === 'Single'" id =" method-single" 
314+                                     <option  v-for =" method of methods" option >
315+                                 </select >
316+                                 <div  class =" form-check" v-for =" m of methods" v-if =" filter.method.state === 'Multi'" 
317+                                     <input  class =" form-check-input" name =" method" :value =" m" v-model =" method" type =" checkbox" :id =" getId(m)" 
318+                                     <label  class =" form-check-label" :for =" getId(m)" label >
319+                                 </div >
320+                             </div >
321+                             <div  class =" row mt-2 me-1" v-if =" filter.method.value.includes('CUSTOM')" 
322+                                 <input  type =" text" class =" form-control form-control-sm" id =" method-custom" v-model =" filter.method.custom" placeholder =" LINK CONNECT..." 
323+                             </div >
324+                         </div >
325+                     </div >
326+ 
327+                 </div >
328+             </div >
329+         </div >
330+     </div >
98331</template >
99332
100333<style  scoped>
101334.warning :empty  {
102335    padding 0 ; 
103336} 
337+ .modal-dialog-scrollable  .modal-body  {
338+   min-height calc (100vh   -  200px  ); /*  header + footer spacing */  
339+ } 
104340style >
0 commit comments