11<template >
22 <q-page padding >
3- <q-expansion-item
4- v-model =" state.expanded"
5- expand-separator
6- icon =" sym_s_add_circle"
7- expand-icon =" sym_s_unfold_more"
8- expanded-icon =" sym_s_unfold_less"
9- :label =" `${isUpdate ? 'Update' : 'Add'} Analytics Rule`"
10- header-class =" bg-primary text-white"
11- >
12- <q-card class =" bg-surface column" >
13- <q-card-section class =" row q-col-gutter-md" >
14- <q-input
15- v-model =" state.rule.name"
16- class =" col-12 col-sm-6"
17- label =" Rule Name"
18- filled
19- :rules =" [(val) => !!val || 'Field is required']"
20- />
21-
22- <q-input
23- v-model =" state.rule.type"
24- class =" col-12 col-sm-6"
25- label =" Rule Type"
26- filled
27- :rules =" [(val) => !!val || 'Field is required']"
28- />
29- </q-card-section >
30- <q-card-section class =" row q-col-gutter-md" >
31- <q-select
32- v-model =" state.rule.params.source.collections"
33- class =" col-12 col-sm-6"
34- label =" Source Collection(s)"
35- filled
36- :options =" collectionNames"
37- multiple
38- hint =" Track searches sent to these collections"
39- ></q-select >
40-
41- <q-select
42- v-model =" state.rule.params.destination.collection"
43- class =" col-12 col-sm-6"
44- label =" Destination Collection"
45- filled
46- :options =" collectionNames"
47- ></q-select >
48- </q-card-section >
49- <q-card-section class =" row q-col-gutter-md" >
50- <q-input
51- v-model =" state.rule.params.limit"
52- class =" col-12 col-sm-6"
53- label =" Limit"
54- filled
55- type =" number"
56- min =" 0"
57- />
58- </q-card-section >
59-
60- <q-card-actions align =" right" class =" bg-primary" >
61- <q-btn size =" md" padding =" sm lg" unelevated color =" primary" @click =" createRule()"
62- >{{ isUpdate ? 'Update' : 'Add' }} Rule</q-btn
3+ <q-list bordered class =" rounded-borders" >
4+ <q-expansion-item
5+ v-model =" state.expanded"
6+ expand-separator
7+ icon =" sym_s_add_circle"
8+ expand-icon =" sym_s_unfold_more"
9+ expanded-icon =" sym_s_unfold_less"
10+ :label =" `${isUpdate ? 'Update' : 'Add'} Analytics Rule`"
11+ header-class =" bg-primary text-white"
12+ >
13+ <q-card >
14+ <q-tabs
15+ v-model =" tab"
16+ dense
17+ class =" text-grey"
18+ active-color =" primary"
19+ indicator-color =" primary"
20+ align =" justify"
21+ narrow-indicator
6322 >
64- </q-card-actions >
65- </q-card >
66- </q-expansion-item >
23+ <q-tab name =" form" label =" Form Mode" />
24+ <q-tab name =" json" label =" JSON Mode" />
25+ </q-tabs >
26+ <q-separator />
27+ <q-tab-panels v-model =" tab" animated class =" bg-surface" >
28+ <q-tab-panel name =" form" >
29+ <q-card-section class =" row q-col-gutter-md" >
30+ <q-input
31+ v-model =" state.rule.name"
32+ class =" col-12 col-sm-6"
33+ label =" Rule Name"
34+ filled
35+ :rules =" [(val) => !!val || 'Field is required']"
36+ />
37+ <q-select
38+ v-model =" state.rule.type"
39+ :options =" typeOptions"
40+ class =" col-12 col-sm-6"
41+ label =" Rule Type"
42+ filled
43+ :rules =" [(val) => !!val || 'Field is required']"
44+ />
45+ </q-card-section >
46+ <q-card-section class =" row q-col-gutter-md" >
47+ <q-select
48+ v-model =" state.rule.params.source.collections"
49+ class =" col-12 col-sm-6"
50+ label =" Source Collection(s)"
51+ filled
52+ :options =" sourceOptions"
53+ multiple
54+ hint =" Track searches sent to these collections or aliases"
55+ ></q-select >
56+ <q-select
57+ v-model =" state.rule.params.destination.collection"
58+ class =" col-12 col-sm-6"
59+ label =" Destination Collection"
60+ filled
61+ :options =" sourceOptions"
62+ ></q-select >
63+ </q-card-section >
64+ <q-card-section class =" row q-col-gutter-md" >
65+ <q-input
66+ v-model =" state.rule.params.limit"
67+ class =" col-12 col-sm-6"
68+ label =" Limit"
69+ filled
70+ type =" number"
71+ min =" 0"
72+ />
73+ <q-checkbox
74+ v-model =" state.rule.params.expand_query"
75+ class =" col-12 col-sm-6"
76+ label =" Expand Query"
77+ filled
78+ />
79+ </q-card-section >
80+ </q-tab-panel >
81+ <q-tab-panel name =" json" class =" q-pa-none" >
82+ <monaco-editor v-model =" ruleJson" style =" height : 60vh " ></monaco-editor >
83+ <q-banner v-if =" jsonError" inline-actions class =" text-white bg-red" >
84+ Invalid Format: {{ jsonError }}
85+ </q-banner >
86+ </q-tab-panel >
87+ </q-tab-panels >
88+ <q-separator />
89+ <q-card-actions align =" right" class =" bg-primary" >
90+ <q-btn size =" md" padding =" sm lg" unelevated color =" primary" @click =" createRule()" >
91+ {{ isUpdate ? 'Update' : 'Add' }} Rule
92+ </q-btn >
93+ </q-card-actions >
94+ </q-card >
95+ </q-expansion-item >
96+ </q-list >
6797
6898 <q-table
6999 class =" q-mt-md"
105135<script setup lang="ts">
106136import { useNodeStore } from ' src/stores/node' ;
107137import type { AnalyticsRuleSchema } from ' typesense/lib/Typesense/AnalyticsRule' ;
108- import { computed , onMounted , reactive } from ' vue' ;
138+ import { computed , onMounted , reactive , ref } from ' vue' ;
109139import type { QTableProps } from ' quasar' ;
110140import { useQuasar } from ' quasar' ;
141+ import MonacoEditor from ' src/components/MonacoEditor.vue' ;
111142
112143const $q = useQuasar ();
113144const store = useNodeStore ();
@@ -123,6 +154,7 @@ function initialData(): AnalyticsRuleSchema {
123154 destination: {
124155 collection: ' ' ,
125156 },
157+ expand_query: false ,
126158 limit: 100 ,
127159 },
128160 };
@@ -177,10 +209,35 @@ const state = reactive({
177209});
178210
179211const collectionNames = computed (() => store .data .collections .map ((collection ) => collection .name ));
212+ const aliasNames = computed (() => store .data .aliases .map ((alias ) => alias .name ));
213+ // Combine and sort collections and aliases for dropdowns
214+ const sourceOptions = computed (() => {
215+ const options = [... collectionNames .value , ... aliasNames .value ];
216+ return options .sort ();
217+ });
218+
219+ const typeOptions = ref ([' popular_queries' , ' nohits_queries' , ' counter' ]);
220+
180221const isUpdate = computed (() =>
181222 store .data .analyticsRules .map ((a ) => a .name ).includes (state .rule .name ),
182223);
183224
225+ const tab = ref (' form' );
226+ const ruleJson = computed ({
227+ get() {
228+ return JSON .stringify (state .rule , null , 2 );
229+ },
230+ set(json : string ) {
231+ try {
232+ state .rule = JSON .parse (json );
233+ jsonError .value = null ;
234+ } catch (e ) {
235+ jsonError .value = (e as Error ).message ;
236+ }
237+ },
238+ });
239+ const jsonError = ref <string | null >(null );
240+
184241async function createRule() {
185242 await store .createAnalyticsRule (state .rule );
186243 state .expanded = false ;
0 commit comments