@drax/settings-vue 0.40.0 → 0.42.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.40.0",
6
+ "version": "0.42.2",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,10 +24,11 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^0.40.0",
28
- "@drax/identity-vue": "^0.40.0",
29
- "@drax/settings-front": "^0.40.0",
30
- "@drax/settings-share": "^0.40.0"
27
+ "@drax/common-front": "^0.42.2",
28
+ "@drax/crud-vue": "^0.42.2",
29
+ "@drax/identity-vue": "^0.42.2",
30
+ "@drax/settings-front": "^0.42.2",
31
+ "@drax/settings-share": "^0.42.2"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "pinia": "^2.2.2",
@@ -64,5 +65,5 @@
64
65
  "vue-tsc": "^2.1.10",
65
66
  "vuetify": "^3.7.1"
66
67
  },
67
- "gitHead": "269ec7921d83364dfba091cd74bd3f45a8135c26"
68
+ "gitHead": "6b4f9f50a8e3f0fbdaff7ec913356834a4e2c0b5"
68
69
  }
@@ -0,0 +1,396 @@
1
+ <script setup lang="ts">
2
+ import {useSetting} from "../composables/UseSetting";
3
+ import {onMounted, reactive, ref} from "vue";
4
+ import {useI18n} from "vue-i18n";
5
+ import type {ISetting} from "@drax/settings-share";
6
+ import dayjs from 'dayjs'
7
+ import SettingField from "./SettingField.vue";
8
+
9
+ const {fetchSettings, settingsGrouped, updateSettingValue} = useSetting()
10
+ const {t} = useI18n()
11
+
12
+ onMounted(async () => {
13
+ await fetchSettings()
14
+ onSearchUpdate('')
15
+ })
16
+
17
+
18
+
19
+ function getTypeColor(type: string): string {
20
+
21
+ //'string' | 'longString' | 'number' | 'enum' | 'boolean' | 'password' |'stringList' | 'numberList' | 'enumList' |'ref' |'secret'
22
+ switch (type) {
23
+ case 'string': return 'blue';
24
+ case 'longString': return 'blue';
25
+ case 'number': return 'purple';
26
+ case 'enum': return 'orange';
27
+ case 'boolean': return 'green';
28
+ case 'password': return 'red';
29
+ case 'stringList': return 'light-blue';
30
+ case 'numberList': return 'purple';
31
+ case 'enumList': return 'orange';
32
+ case 'ref': return 'pink';
33
+ case 'secret': return 'error';
34
+ default: return 'grey';
35
+ }
36
+ }
37
+
38
+
39
+
40
+
41
+
42
+ const settingsGroupedFiltered = ref<Record<string, ISetting[]>>({})
43
+
44
+ const search = ref<string>('')
45
+ function onSearchUpdate(value: string) {
46
+ if(!value) settingsGroupedFiltered.value = {...settingsGrouped.value};
47
+
48
+ for (const groupKey of Object.keys(settingsGroupedFiltered.value)) {
49
+ settingsGroupedFiltered.value[groupKey] = settingsGroupedFiltered.value[groupKey].filter((setting: ISetting) =>
50
+ setting.key.toLowerCase().includes(value.toLowerCase()) ||
51
+ (setting.description && setting.description.toLowerCase().includes(value.toLowerCase()))
52
+ )
53
+
54
+ if(!settingsGroupedFiltered.value[groupKey]?.length) {
55
+ delete settingsGroupedFiltered.value[groupKey];
56
+ }
57
+ }
58
+ }
59
+
60
+ const showModal = reactive<{
61
+ isOpen: boolean, value?: string,
62
+ setting?: ISetting
63
+ }>({ isOpen: false, value: '', setting: undefined})
64
+
65
+ const openShowModal = (key: string, value: string | undefined, setting: ISetting) => {
66
+ showModal.isOpen = true;
67
+ showModal.value = value;
68
+ showModal.setting = setting
69
+ }
70
+
71
+ const closeShowModal = () => {
72
+ showModal.isOpen = false;
73
+ showModal.value = '';
74
+ }
75
+
76
+ const getSettingDescription = (key: string, description?: string) => {
77
+ return {
78
+ _id: "123",
79
+ category: "Description",
80
+ key: key,
81
+ label: `${t('setting.descriptionof')} ${key}`,
82
+ type: "longString",
83
+ value: description
84
+ } as ISetting
85
+ }
86
+
87
+ const informationUpdatingModal = reactive<{
88
+ isOpen: boolean, setting?: ISetting, readonly: boolean
89
+ }>({isOpen: false, readonly: false})
90
+
91
+ const openInformationUpdatingModal = (setting: ISetting, readonly: boolean) => {
92
+ informationUpdatingModal.isOpen = true
93
+ informationUpdatingModal.setting = setting
94
+ informationUpdatingModal.readonly = readonly
95
+ }
96
+
97
+ const closeInformationUpdatingModal = () => {
98
+ informationUpdatingModal.isOpen = false
99
+ }
100
+
101
+ const updateLoading = ref<boolean>(false)
102
+
103
+ const form = ref()
104
+
105
+ const alert = reactive<{
106
+ value: boolean, type: 'error' | 'success' | 'warning',
107
+ text: string
108
+ }>({value: false, type: 'success', text: ''})
109
+
110
+ const showSuccess = (text: string) => {
111
+ alert.value = true
112
+ alert.type = "success"
113
+ alert.text = text
114
+ }
115
+
116
+ const updateSetting = async (setting?: ISetting) => {
117
+ try {
118
+ if(!setting){
119
+ console.error('[updateSetting] - setting not provided')
120
+ return
121
+ }
122
+ updateLoading.value = true
123
+ const validation = await form.value.validate()
124
+ if(validation && validation.valid === true){
125
+ const settingUpdated = await updateSettingValue(setting._id, setting.value)
126
+ setting.updatedBy = settingUpdated.updatedBy
127
+ }
128
+ closeInformationUpdatingModal()
129
+ showSuccess(t('setting.updated.successfuly'))
130
+ } catch (e) {
131
+ console.error(e)
132
+ } finally {
133
+ updateLoading.value = false
134
+ }
135
+ }
136
+
137
+ </script>
138
+
139
+ <template>
140
+ <div class="d-flex align-center px-16">
141
+ <v-avatar style="border-radius: 7px;" color="menuIcon" class="mr-4">
142
+ <v-icon>mdi-cog</v-icon>
143
+ </v-avatar>
144
+ <v-toolbar-title>{{ t('setting.page.title') }}</v-toolbar-title>
145
+ </div>
146
+ <v-divider class="my-4"></v-divider>
147
+ <div class="d-flex justify-space-between px-16">
148
+ <v-text-field
149
+ :placeholder="t('setting.search')"
150
+ persistent-placeholder
151
+ clearable variant="outlined"
152
+ prepend-inner-icon="mdi-magnify"
153
+ v-model="search"
154
+ @update:model-value="onSearchUpdate"
155
+ ></v-text-field>
156
+ </div>
157
+ <div
158
+ class="px-16"
159
+ >
160
+ <div class="pa-0 ma-0" style="width: calc(100%); height: calc(100vh - 315px); overflow-y: auto; scrollbar-width: thin;">
161
+ <v-expansion-panels
162
+ class="pr-1 mb-4" variant="accordion" v-for="groupkey of Object.keys(settingsGroupedFiltered)" :key="groupkey"
163
+ :model-value="1"
164
+ >
165
+ <v-expansion-panel :value="1">
166
+ <template #title>
167
+ {{ groupkey }}
168
+ <v-avatar class="ml-4" color="blue-grey-darken-2" style="border-radius: 4px;" size="24">
169
+ {{ settingsGroupedFiltered[groupkey]?.length }}
170
+ </v-avatar>
171
+ </template>
172
+ <template #text>
173
+ <table style="width: 100%;">
174
+ <thead>
175
+ <v-row>
176
+ <v-col cols="2" class="text-caption">{{t('setting.field.key')}}</v-col>
177
+ <v-col cols="1" class="text-caption">{{t('setting.field.value')}}</v-col>
178
+ <v-col cols="1" class="text-caption">{{t('setting.field.type')}}</v-col>
179
+ <v-col cols="3" class="text-caption">{{t('setting.field.description')}}</v-col>
180
+ <v-col cols="1" class="text-caption">{{t('setting.field.visibility')}}</v-col>
181
+ <v-col cols="1" class="text-caption">{{t('setting.field.permission')}}</v-col>
182
+ <v-col cols="2" class="text-caption">{{t('setting.field.updated')}}</v-col>
183
+ <v-col cols="1"></v-col>
184
+ </v-row>
185
+ </thead>
186
+ <tbody>
187
+ <template :key="setting.key" v-for="setting in settingsGroupedFiltered[groupkey]">
188
+ <v-row class="mt-2">
189
+ <v-col cols="2" class="pr-4">
190
+ <span
191
+ style="display: inline-block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
192
+ >
193
+ {{ setting.key }}
194
+ </span>
195
+ </v-col>
196
+ <v-col cols="1" class="pr-4 d-flex ga-2">
197
+ <template v-if="setting.value">
198
+ <v-icon size="small" color="grey" style="cursor: pointer;" @click="openShowModal(setting.key, setting.value, setting)">mdi-eye</v-icon>
199
+ <span
200
+ class="text-caption text-grey"
201
+ style="display: inline-block; max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
202
+ >
203
+ {{ setting.value }}
204
+ </span>
205
+ </template>
206
+ <span v-else class="text-caption text-grey">-</span>
207
+ </v-col>
208
+ <v-col cols="1" class="pr-4">
209
+ <v-chip :color="getTypeColor(setting.type)" density="compact" style="border-radius: 7px">
210
+ {{ setting.type }}
211
+ </v-chip>
212
+ </v-col>
213
+ <v-col cols="3" class="pr-4 d-flex ga-2">
214
+ <v-icon size="small" color="grey" style="cursor: pointer;" @click="openShowModal(setting.key, setting.description, getSettingDescription(setting.key, setting.description))">mdi-eye</v-icon>
215
+ <span
216
+ class="text-caption text-grey"
217
+ style="display: inline-block; max-width: 700px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
218
+ >
219
+ {{ setting.description }}
220
+ </span>
221
+ </v-col>
222
+ <v-col cols="1" class="pr-4">
223
+ <v-chip
224
+ :color="setting.public ? 'green' : 'red'"
225
+ density="compact" style="border-radius: 7px;"
226
+ >
227
+ <v-icon start>{{ setting.public ? 'mdi-web' : 'mdi-lock' }}</v-icon>
228
+ {{ setting.public ? 'Público' : 'Privado' }}
229
+ </v-chip>
230
+ </v-col>
231
+ <v-col cols="1" class="pr-4">
232
+ <span class="text-caption" style="display: inline-block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
233
+ {{ setting.permission ? setting.permission : 'N/A' }}
234
+ </span>
235
+ </v-col>
236
+ <v-col cols="2" class="pr-4">
237
+ <span>
238
+ {{ dayjs(setting.updatedAt).format('DD/MM/YYYY HH:mm') }}<br>
239
+ {{ setting.updatedBy }}
240
+ </span>
241
+ </v-col>
242
+ <v-col cols="1" class="d-flex justify-end">
243
+ <v-menu location="bottom right">
244
+ <template v-slot:activator="{ props }">
245
+ <v-btn icon size="small" variant="text" v-bind="props">
246
+ <v-icon>mdi-dots-vertical</v-icon>
247
+ </v-btn>
248
+ </template>
249
+ <v-list>
250
+ <v-list-item @click="openInformationUpdatingModal(setting, true)">
251
+ <v-icon start>mdi-eye</v-icon>
252
+ {{ t('action.view') }}
253
+ </v-list-item>
254
+ <v-list-item @click="openInformationUpdatingModal(setting, false)">
255
+ <v-icon start>mdi-pencil</v-icon>
256
+ {{ t('action.edit') }}
257
+ </v-list-item>
258
+ </v-list>
259
+ </v-menu>
260
+ </v-col>
261
+ </v-row>
262
+ <v-divider class="my-2"></v-divider>
263
+ </template>
264
+ </tbody>
265
+ </table>
266
+ </template>
267
+ </v-expansion-panel>
268
+ </v-expansion-panels>
269
+ </div>
270
+ </div>
271
+
272
+ <!-- SHOW MODAL -->
273
+
274
+ <v-dialog v-model="showModal.isOpen" width="auto">
275
+ <v-card width="800">
276
+ <v-toolbar class="pl-4">
277
+ <v-icon start>mdi-information</v-icon>
278
+ <v-card-title>{{ showModal.setting?.category === 'Description' ? t('setting.field.description') : t('setting.field.value') }}</v-card-title>
279
+ <v-spacer></v-spacer>
280
+ <v-btn text @click="closeShowModal">
281
+ <v-icon>mdi-close</v-icon>
282
+ </v-btn>
283
+ </v-toolbar>
284
+ <v-card-text>
285
+ <!-- @vue-ignore -->
286
+ <setting-field
287
+ :model-value="showModal.value"
288
+ :setting="showModal.setting"
289
+ :editing="false"
290
+ ></setting-field>
291
+ </v-card-text>
292
+ </v-card>
293
+ </v-dialog>
294
+
295
+ <!-- INFORMATION / UPDATING MODAL -->
296
+
297
+ <v-dialog v-model="informationUpdatingModal.isOpen" width="auto">
298
+ <v-card width="1200">
299
+ <v-toolbar class="px-4">
300
+ <v-icon start>{{ informationUpdatingModal.readonly ? 'mdi-eye' : 'mdi-pencil' }}</v-icon>
301
+ <v-card-title>
302
+ {{ informationUpdatingModal.readonly ? t('action.view') : t('action.edit') }} {{t('setting.configurationValue.title')}}
303
+ </v-card-title>
304
+ <v-spacer></v-spacer>
305
+ <v-icon size="small" @click="closeInformationUpdatingModal">mdi-close</v-icon>
306
+ </v-toolbar>
307
+ <v-card-text>
308
+ {{ t('setting.configurationValue.subtitle') }}
309
+
310
+ <v-form ref="form">
311
+ <v-row class="mt-5">
312
+ <v-col cols="12" md="8" class="ma-0 pa-0 px-3" v-if="informationUpdatingModal.setting?.category">
313
+ <v-text-field
314
+ density="compact"
315
+ variant="outlined"
316
+ :label="t('setting.field.category')"
317
+ readonly :model-value="informationUpdatingModal.setting?.category"
318
+ ></v-text-field>
319
+ </v-col>
320
+ <v-col cols="12" md="4" class="ma-0 pa-0 px-3">
321
+ <v-checkbox
322
+ :label="t('setting.field.publicinfo')" density="compact"
323
+ readonly :model-value="informationUpdatingModal.setting?.public"
324
+ ></v-checkbox>
325
+ </v-col>
326
+ <v-col cols="12" md="8" class="ma-0 pa-0 px-3">
327
+ <v-text-field
328
+ density="compact"
329
+ variant="outlined"
330
+ :label="t('setting.field.key')"
331
+ readonly :model-value="informationUpdatingModal.setting?.key"
332
+ ></v-text-field>
333
+ </v-col>
334
+ <v-col cols="12" md="4" class="ma-0 pa-0 px-3">
335
+ <v-text-field
336
+ density="compact"
337
+ variant="outlined"
338
+ :label="t('setting.field.type')"
339
+ readonly :model-value="informationUpdatingModal.setting?.type"
340
+ ></v-text-field>
341
+ </v-col>
342
+ <v-col cols="12" md="12" class="ma-0 pa-0 px-3" v-if="informationUpdatingModal.setting?.permission">
343
+ <v-text-field
344
+ density="compact"
345
+ variant="outlined"
346
+ :label="t('setting.field.permission')"
347
+ readonly :model-value="informationUpdatingModal.setting?.permission"
348
+ ></v-text-field>
349
+ </v-col>
350
+ <v-col cols="12" md="6" class="ma-0 pa-0 px-3" v-if="informationUpdatingModal.setting?.prefix">
351
+ <v-text-field
352
+ density="compact"
353
+ variant="outlined"
354
+ :label="t('setting.field.prefix')"
355
+ readonly :model-value="informationUpdatingModal.setting?.prefix"
356
+ ></v-text-field>
357
+ </v-col>
358
+ <v-col cols="12" md="6" class="ma-0 pa-0 px-3" v-if="informationUpdatingModal.setting?.suffix">
359
+ <v-text-field
360
+ density="compact"
361
+ variant="outlined"
362
+ :label="t('setting.field.suffix')"
363
+ readonly :model-value="informationUpdatingModal.setting?.suffix"
364
+ ></v-text-field>
365
+ </v-col>
366
+ <v-col cols="12" md="12" class="ma-0 pa-0 px-3">
367
+ <!-- @vue-ignore -->
368
+ <setting-field
369
+ variant="outlined"
370
+ v-model="informationUpdatingModal.setting.value"
371
+ :setting="informationUpdatingModal.setting"
372
+ :editing="!informationUpdatingModal.readonly"
373
+ ></setting-field>
374
+ </v-col>
375
+ </v-row>
376
+ </v-form>
377
+ </v-card-text>
378
+ <v-card-actions>
379
+ <v-spacer></v-spacer>
380
+ <v-btn variant="text" @click="closeInformationUpdatingModal">{{ t('action.close') }}</v-btn>
381
+ <v-btn
382
+ v-if="!informationUpdatingModal.readonly"
383
+ variant="elevated" color="primary"
384
+ @click="updateSetting(informationUpdatingModal.setting)"
385
+ :loading="updateLoading"
386
+ >
387
+ {{ t('action.save') }}
388
+ </v-btn>
389
+ </v-card-actions>
390
+ </v-card>
391
+ </v-dialog>
392
+ </template>
393
+
394
+ <style scoped>
395
+
396
+ </style>
@@ -54,7 +54,7 @@ async function updateValue() {
54
54
  <v-card-text>
55
55
  <v-form v-if="setting" ref="form" autocomplete="off" @submit.prevent>
56
56
  <v-row>
57
- <v-col cols="10">
57
+ <v-col cols="12">
58
58
  <setting-field
59
59
  v-model="value"
60
60
  :setting="setting"
@@ -1,23 +1,21 @@
1
1
  <script setup lang="ts">
2
2
 
3
3
  import type {ISetting} from "@drax/settings-share";
4
+ import {CrudAutocomplete, useEntityStore} from "@drax/crud-vue";
4
5
  import {type PropType, ref, computed} from "vue";
5
6
 
6
7
 
7
8
  const valueModel = defineModel<any>({type: [String, Number, Boolean, Object, Array], default: false})
8
9
 
9
10
 
10
- const {setting, editing} = defineProps({
11
+ const {setting, editing, variant} = defineProps({
11
12
  setting: {type: Object as PropType<ISetting>, required: true},
12
- editing: {type: Boolean as PropType<boolean>, default: false}
13
+ editing: {type: Boolean as PropType<boolean>, default: false},
14
+ variant: {type: String as PropType<'filled' | 'outlined' | 'underlined'>, default: 'filled'},
13
15
  })
14
16
 
15
17
  const visible = ref(false)
16
18
 
17
- const variant = computed(() => {
18
- return editing ? 'filled' : 'underlined'
19
- })
20
-
21
19
  const validateRegex = computed(() => {
22
20
  return [(val: any) => {
23
21
  if (!setting.regex) return true
@@ -49,6 +47,14 @@ const validateNumberList = computed(() => {
49
47
  }]
50
48
  })
51
49
 
50
+ const entityStore = useEntityStore()
51
+
52
+ const getEntity = computed(() => {
53
+ return (entity: string | undefined) => {
54
+ return entity ? entityStore.getEntity(entity) : undefined
55
+ }
56
+ })
57
+
52
58
 
53
59
  </script>
54
60
 
@@ -93,7 +99,7 @@ const validateNumberList = computed(() => {
93
99
 
94
100
 
95
101
  <!--password-->
96
- <v-text-field v-if=" setting.type === 'password'"
102
+ <v-text-field v-if=" setting.type === 'password' || setting.type === 'secret'"
97
103
  :name="setting.key"
98
104
  v-model="valueModel"
99
105
  :label="setting.label"
@@ -224,17 +230,12 @@ const validateNumberList = computed(() => {
224
230
  </v-select>
225
231
 
226
232
  <!--ref-->
227
- <!-- <v-select v-if="setting.type === 'ref'"-->
228
- <!-- prepend-icon="list"-->
229
- <!-- :name="setting.key"-->
230
- <!-- setting-text="entityText"-->
231
- <!-- setting-value="entityValue"-->
232
- <!-- :settings="entityOptions"-->
233
- <!-- v-model="valueModel"-->
234
- <!-- :label="setting.label"-->
235
- <!-- :placeholder="setting.label"-->
236
- <!-- color="secondary">-->
237
- <!-- </v-select>-->
233
+ <crud-autocomplete
234
+ v-if="setting.type ==='ref'"
235
+ v-model="valueModel"
236
+ :entity="getEntity(setting.entity)"
237
+ :field="{name: setting.key, type: 'ref', label: setting.label, default:null}"
238
+ ></crud-autocomplete>
238
239
 
239
240
  </template>
240
241
 
@@ -1,15 +1,17 @@
1
1
  <script setup lang="ts">
2
2
  import {useSetting} from "../composables/UseSetting";
3
3
  import SettingEditor from "./SettingEditor.vue";
4
- import {onMounted, ref} from "vue";
4
+ import {onMounted, ref, computed} from "vue";
5
5
  import {useI18n} from "vue-i18n";
6
6
  import type {ISetting} from "@drax/settings-share";
7
+ import {CrudAutocomplete, useEntityStore} from "@drax/crud-vue";
7
8
 
8
9
  const {fetchSettings, settingsGrouped} = useSetting()
9
10
  const {t, te} = useI18n()
10
11
 
11
12
  onMounted(async () => {
12
13
  await fetchSettings()
14
+ onSearchUpdate('')
13
15
  })
14
16
 
15
17
  const settingEditing = ref()
@@ -21,7 +23,7 @@ function edit(setting: ISetting) {
21
23
  editing.value = true
22
24
  }
23
25
 
24
- function clearEdit() {
26
+ function clearEdit(newValue: any) {
25
27
  editing.value = false
26
28
  settingEditing.value = null
27
29
  }
@@ -38,11 +40,47 @@ function getObfuscatedValue(value: string): string {
38
40
  return '•'.repeat(Math.min(value?.length || 8, 12))
39
41
  }
40
42
 
43
+ const search = ref<string>('')
44
+ function onSearchUpdate(value: string) {
45
+ search.value = value
46
+ }
47
+
48
+ const settingsGroupedFiltered = computed(() => {
49
+ if (!search.value) {
50
+ return settingsGrouped.value
51
+ }
52
+
53
+ const filtered: Record<string, ISetting[]> = {}
54
+ const searchLower = search.value.toLowerCase()
55
+
56
+ for (const groupKey of Object.keys(settingsGrouped.value)) {
57
+ const filteredSettings = settingsGrouped.value[groupKey].filter((setting: ISetting) =>
58
+ setting.key.toLowerCase().includes(searchLower) ||
59
+ (setting.description && setting.description.toLowerCase().includes(searchLower))
60
+ )
61
+
62
+ if (filteredSettings.length > 0) {
63
+ filtered[groupKey] = filteredSettings
64
+ }
65
+ }
66
+
67
+ return filtered
68
+ })
69
+
70
+ const entityStore = useEntityStore()
71
+
72
+ const getEntity = computed(() => {
73
+ return (entity: string | undefined) => {
74
+ return entity ? entityStore.getEntity(entity) : undefined
75
+ }
76
+ })
77
+
41
78
  </script>
42
79
 
43
80
  <template>
44
81
  <div>
45
- <h1 class="mb-6 text-h2">{{t('setting.menu')}}</h1>
82
+ <h3 class="mb-2 text-h3">{{t('setting.menu')}}</h3>
83
+ <v-divider class="mb-3"></v-divider>
46
84
 
47
85
  <setting-editor
48
86
  v-if="editing"
@@ -52,21 +90,30 @@ function getObfuscatedValue(value: string): string {
52
90
  ></setting-editor>
53
91
 
54
92
  <v-row>
55
- <v-col cols="12" v-for="(category,k) in settingsGrouped" class="mt-4">
93
+ <v-col>
94
+ <v-text-field
95
+ :placeholder="t('setting.search')"
96
+ persistent-placeholder
97
+ clearable variant="outlined"
98
+ prepend-inner-icon="mdi-magnify"
99
+ v-model="search"
100
+ @update:model-value="onSearchUpdate"
101
+ ></v-text-field>
102
+ </v-col>
103
+
104
+ <v-col cols="12" v-for="(category,k) in settingsGroupedFiltered">
56
105
  <v-card>
57
106
  <v-card-title>
58
- <h4 class="text-h4 mt-2">{{ k }}</h4>
107
+ <h4 class="text-h4 mt-2 ">{{ k }}</h4>
59
108
  </v-card-title>
60
109
  <v-card-text>
61
110
 
62
111
  <v-data-table
63
112
  hide-default-footer
64
113
  :headers="[
65
- { title: t('setting.field.key'), key: 'key', width: '130px', minWidth: '130px' },
66
- { title: t('setting.field.label'), key: 'label', width: '130px', minWidth: '130px' },
114
+ { title: t('setting.field.variable'), key: 'label', width: '220px', minWidth: '220px', maxWidth: '220px' },
67
115
  { title: t('setting.field.value'), key: 'value' },
68
- { title: t('setting.field.type'), key: 'type', width: '120px', minWidth: '120px' },
69
- { title: t('setting.field.scope'), key:'scope', width: '170px', minWidth: '170px' },
116
+ { title: t('setting.field.config'), key: 'config', width: '220px', minWidth: '220px', maxWidth: '220px' },
70
117
  { title: t('action.edit'), key: 'actions', width: '70px' },
71
118
 
72
119
  ]"
@@ -74,18 +121,25 @@ function getObfuscatedValue(value: string): string {
74
121
 
75
122
  >
76
123
 
124
+
125
+ <template v-slot:item.label="{ item }">
126
+ <span class="font-weight-bold">{{item.label }}</span><br>
127
+ <span class="font-italic">{{ item.description }}</span>
128
+ </template>
129
+
77
130
  <template v-slot:item.value="{ item }">
78
- <v-card variant="tonal" density="compact" class="my-1"><v-card-text>
79
- {{ item.prefix }}
80
- <template v-if="item.type === 'boolean'">
81
- <v-chip :color="item.value ? 'green' : 'red' " tile>
82
- {{ item.value }}
83
- </v-chip>
84
- </template>
85
- <template v-else-if="['stringList','numberList','enumList'].includes(item.type)">
86
- <v-chip v-for="(v,i) in item.value" :key="i">{{v}}</v-chip>
87
- </template>
88
- <template v-else-if="['password','secret'].includes(item.type)">
131
+ <v-card :variant="['stringList','numberList','enumList','boolean','ref'].includes(item.type) ? 'flat' : 'tonal'" density="compact" class="my-1">
132
+ <v-card-text>
133
+ {{ item.prefix }}
134
+ <template v-if="item.type === 'boolean'">
135
+ <v-chip size="large" :color="item.value ? 'green' : 'red' " tile>
136
+ {{ item.value }}
137
+ </v-chip>
138
+ </template>
139
+ <template v-else-if="['stringList','numberList','enumList'].includes(item.type)">
140
+ <v-chip v-for="(v,i) in item.value" :key="i">{{v}}</v-chip>
141
+ </template>
142
+ <template v-else-if="['password','secret'].includes(item.type)">
89
143
  <span class="d-inline-flex align-center">
90
144
  <span class="mr-2">
91
145
  {{ isSecretVisible(item.key) ? item.value : getObfuscatedValue(item.value) }}
@@ -97,26 +151,42 @@ function getObfuscatedValue(value: string): string {
97
151
  @click="toggleSecretVisibility(item.key)"
98
152
  ></v-btn>
99
153
  </span>
100
- </template>
101
- <template v-else>
102
- {{item.value}}
103
- </template>
104
- {{ item.suffix }}
105
- </v-card-text></v-card>
154
+ </template>
155
+ <template v-else-if="['ref'].includes(item.type)">
156
+ <!--ref-->
157
+ <crud-autocomplete
158
+ readonly
159
+ v-model="item.value"
160
+ :entity="getEntity(item.entity)"
161
+ :field="{name: item.key, type: 'ref', label: item.label, default:null}"
162
+ :clearable="false"
163
+ ></crud-autocomplete>
164
+ </template>
165
+ <template v-else>
166
+ {{item.value}}
167
+ </template>
168
+ {{ item.suffix }}
169
+ </v-card-text>
170
+ </v-card>
106
171
  </template>
107
172
 
108
- <template v-slot:item.scope="{ item }">
109
- <div style="width: 200px;">
110
- <v-chip v-if="item?.permission" density="compact" color="purple">
173
+ <template v-slot:item.config="{ item }">
174
+ <div class="py-1">
175
+ <span class="font-weight-bold">{{item.key }}</span><br>
176
+ <span class="font-italic">{{ item.type }}</span>
177
+ <div>
178
+ <v-chip tile v-if="item?.permission" density="compact" color="purple" prepend-icon="mdi-key">
111
179
  {{
112
180
  te('permission.' + item?.permission) ? t('permission.' + item?.permission) : item?.permission
113
181
  }}
114
182
  </v-chip>
115
- <v-chip v-else-if="item?.public" color="blue" density="compact">Public</v-chip>
116
- <v-chip v-else color="orange" density="compact">Private</v-chip>
183
+ <v-chip tile v-else-if="item?.public" color="blue" density="compact" prepend-icon="mdi-earth">Public</v-chip>
184
+ <v-chip tile v-else color="orange" density="compact" prepend-icon="mdi-lock">Private</v-chip>
185
+ </div>
117
186
  </div>
118
187
  </template>
119
188
 
189
+
120
190
  <template v-slot:item.actions="{ item }">
121
191
  <v-btn
122
192
  class="mr-1 mt-1"
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import {useSetting} from "./composables/UseSetting";
2
2
  import {useSettingStore} from "./stores/UseSettingStore";
3
3
  import SettingCardConfig from "./components/SettingCardConfig.vue";
4
- import SettingTableConfig from "./components/SettingTableConfig.vue";
4
+ import SettingTableConfig from "./components/SettingAvConfig.vue";
5
5
  import SettingLoaded from "./components/SettingLoaded.vue";
6
6
  import SettingPage from "./pages/SettingPage.vue";
7
7
  import SettingRoutes from "./routes/SettingRoutes";
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import SettingAvConfig from "../components/SettingAvConfig.vue";
3
+ </script>
4
+
5
+ <template>
6
+ <v-container fluid>
7
+ <setting-av-config></setting-av-config>
8
+ </v-container>
9
+ </template>
10
+
11
+ <style scoped>
12
+
13
+ </style>
@@ -3,10 +3,9 @@ import SettingTableConfig from "../components/SettingTableConfig.vue";
3
3
  </script>
4
4
 
5
5
  <template>
6
- <v-container >
6
+ <v-container fluid>
7
7
  <setting-table-config></setting-table-config>
8
8
  </v-container>
9
-
10
9
  </template>
11
10
 
12
11
  <style scoped>
@@ -1,5 +1,6 @@
1
1
  import SettingPage from '../pages/SettingPage.vue'
2
2
  import SettingCardPage from '../pages/SettingCardPage.vue'
3
+ import SettingAvPage from '../pages/SettingAvPage.vue'
3
4
 
4
5
  const routes = [
5
6
  {
@@ -20,7 +21,15 @@ const routes = [
20
21
  permission: 'setting:manage'
21
22
  }
22
23
  },
23
-
24
+ {
25
+ name: 'SettingAvPage',
26
+ path: '/settings/av',
27
+ component: SettingAvPage,
28
+ meta: {
29
+ auth: true,
30
+ permission: 'setting:manage'
31
+ }
32
+ },
24
33
  ]
25
34
 
26
35