@hostlink/nuxt-light 1.5.2 → 1.6.1

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/dist/module.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.5.2"
4
+ "version": "1.6.1"
5
5
  }
@@ -18,7 +18,7 @@ const localValue = computed({
18
18
  });
19
19
  </script>
20
20
  <template>
21
- <q-checkbox v-bind="$props" v-model="localValue" :label="$t($props.label ?? '')">
21
+ <q-checkbox v-bind="$props" v-model="localValue" :label="$t($props.label ?? '')" :color="$light.color">
22
22
  <slot></slot>
23
23
  </q-checkbox>
24
24
  </template>
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { computed, ref } from 'vue'
2
+ import { ref } from 'vue'
3
3
  const menuWidth = defineModel('menuWidth', {
4
4
  type: Number,
5
5
  default: 280
@@ -35,7 +35,6 @@ const COLORS = [
35
35
  ]
36
36
 
37
37
 
38
-
39
38
  defineEmits(['update:theme', 'update:menuOverlayHeader', 'update:dense', 'update:color', 'update:miniState', 'update:footer'])
40
39
 
41
40
  const props = defineProps({
@@ -161,12 +160,12 @@ setInterval(() => {
161
160
  </q-item-section>
162
161
  </q-item>
163
162
  <q-separator />
164
- <q-item-label header>{{ $t('Menu width') }}</q-item-label>
165
163
  <q-item>
166
164
  <q-item-section>
167
- <q-slider v-model="menuWidth" :min="200" :max="360" label :color="$light.color" />
165
+ <q-item-label>{{ $t('Menu width') }}</q-item-label>
166
+ <q-item-label><q-slider v-model="menuWidth" :min="200" :max="360" label
167
+ :color="$light.color" /></q-item-label>
168
168
  </q-item-section>
169
-
170
169
  </q-item>
171
170
  <q-separator />
172
171
 
@@ -1,55 +1,56 @@
1
1
  <script setup>
2
-
3
- const props = defineProps({
4
- modelValue: {
5
- type: String,
6
- default: ""
7
- }
2
+ const modelValue = defineModel({
3
+ type: String,
4
+ default: ""
8
5
  })
9
-
10
6
  const emit = defineEmits(['update:modelValue'])
11
7
  const toggleLabel = (type) => {
12
-
13
- if (props.modelValue == type) {
8
+ if (modelValue.value == type) {
14
9
  emit('update:modelValue', null)
15
10
  } else {
16
11
  emit('update:modelValue', type)
17
12
  }
18
13
  }
19
14
 
15
+ const LABLES = [
16
+ {
17
+ type: 'document',
18
+ icon: 'sym_o_article',
19
+ label: 'Documents'
20
+ },
21
+ {
22
+ type: 'image',
23
+ icon: 'sym_o_insert_photo',
24
+ label: 'Images'
25
+ },
26
+ {
27
+ type: 'video',
28
+ icon: 'sym_o_videocam',
29
+ label: 'Videos'
30
+ },
31
+ {
32
+ type: 'audio',
33
+ icon: 'sym_o_audiotrack',
34
+ label: 'Audio'
35
+ },
36
+ {
37
+ type: 'other',
38
+ icon: 'sym_o_text_snippet',
39
+ label: 'Archives'
40
+ }
41
+ ]
42
+
20
43
  </script>
21
44
  <template>
22
45
  <q-item-label header>
23
46
  {{ $t('Labels') }}
24
47
  </q-item-label>
25
- <q-item clickable :active="modelValue == 'document'" @click="toggleLabel('document')">
26
- <q-item-section avatar>
27
- <q-icon name="sym_o_article" />
28
- </q-item-section>
29
- <q-item-section> {{ $t('Documents') }} </q-item-section>
30
- </q-item>
31
- <q-item clickable :active="modelValue == 'image'" @click="toggleLabel('image')">
32
- <q-item-section avatar>
33
- <q-icon name="sym_o_insert_photo" />
34
- </q-item-section>
35
- <q-item-section> {{ $t('Images') }} </q-item-section>
36
- </q-item>
37
- <q-item clickable :active="modelValue == 'video'" @click="toggleLabel('video')">
38
- <q-item-section avatar>
39
- <q-icon name="sym_o_videocam" />
40
- </q-item-section>
41
- <q-item-section> {{ $t('Videos') }} </q-item-section>
42
- </q-item>
43
- <q-item clickable :active="modelValue == 'audio'" @click="toggleLabel('audio')">
44
- <q-item-section avatar>
45
- <q-icon name="sym_o_audiotrack" />
46
- </q-item-section>
47
- <q-item-section> {{ $t('Audio') }} </q-item-section>
48
- </q-item>
49
- <q-item clickable :active="modelValue == 'other'" @click="toggleLabel('other')">
48
+
49
+ <q-item v-for="label in LABLES" :key="label.type" clickable :active="modelValue == label.type"
50
+ :active-class="`text-${$light.color}`" @click="toggleLabel(label.type)">
50
51
  <q-item-section avatar>
51
- <q-icon name="sym_o_text_snippet" />
52
+ <q-icon :name="label.icon" />
52
53
  </q-item-section>
53
- <q-item-section> {{ $t('Archives') }} </q-item-section>
54
+ <q-item-section> {{ $t(label.label) }} </q-item-section>
54
55
  </q-item>
55
56
  </template>
@@ -1,5 +1,8 @@
1
1
  <script setup>
2
+ import { format } from "quasar"
2
3
  import { q } from '../';
4
+ const { humanStorageSize } = format
5
+
3
6
  const props = defineProps(["modelValue"]);
4
7
 
5
8
 
@@ -7,6 +10,8 @@ const file = await q("fsFile", {
7
10
  path: props.modelValue.path
8
11
  }, ["canPreview", "imagePath"])
9
12
 
13
+ const size = humanStorageSize(props.modelValue.size)
14
+
10
15
  </script >
11
16
  <template>
12
17
  <template v-if="file.canPreview">
@@ -17,19 +22,31 @@ const file = await q("fsFile", {
17
22
  <q-item-section>
18
23
  <q-item-label>Name</q-item-label>
19
24
  </q-item-section>
20
- <q-item-section side>{{ modelValue.name }} </q-item-section>
25
+ <q-item-section side>
26
+ <q-item-label lines="1">
27
+ {{ modelValue.name }}
28
+ </q-item-label>
29
+ </q-item-section>
21
30
  </q-item>
22
31
  <q-item>
23
32
  <q-item-section>
24
33
  <q-item-label>Size</q-item-label>
25
34
  </q-item-section>
26
- <q-item-section side>{{ modelValue.size }} </q-item-section>
35
+ <q-item-section side>{{ size }} ({{ modelValue.size }}) </q-item-section>
27
36
  </q-item>
28
37
  <q-item>
29
38
  <q-item-section>
30
39
  <q-item-label>Location</q-item-label>
31
40
  </q-item-section>
32
- <q-item-section side>{{ modelValue.path }} </q-item-section>
41
+ <q-item-section side>
42
+ <q-item-label lines="1">{{ modelValue.path }}</q-item-label>
43
+ </q-item-section>
44
+ </q-item>
45
+ <q-item>
46
+ <q-item-section>
47
+ <q-item-label>Last modified</q-item-label>
48
+ </q-item-section>
49
+ <q-item-section side>{{ modelValue.lastModifiedHuman }}</q-item-section>
33
50
  </q-item>
34
51
  </q-list>
35
52
  </template>
@@ -2,14 +2,14 @@
2
2
  import { VariableType } from "json-to-graphql-query";
3
3
  import { useI18n } from "vue-i18n";
4
4
  import { ref, watch, computed } from 'vue';
5
- import { useQuasar } from 'quasar';
5
+ import { useQuasar, format } from 'quasar';
6
6
  import { q, m, useLight } from '#imports';
7
7
  import {
8
8
  fsListFolders, fsCreateFolder, fsDeleteFolder, fsDeleteFile, fsRenameFile, fsRenameFolder, fsReadFile,
9
9
  granted
10
10
 
11
11
  } from "@hostlink/light";
12
-
12
+ const { humanStorageSize } = format
13
13
 
14
14
  const light = useLight();
15
15
  const i18n = useI18n();
@@ -17,7 +17,7 @@ const quasar = useQuasar();
17
17
  const emit = defineEmits(["input", "close"]);
18
18
 
19
19
  const props = defineProps({
20
- closable: Boolean,
20
+ closeable: Boolean,
21
21
  height: {
22
22
  type: String,
23
23
  default: "700px",
@@ -60,14 +60,20 @@ const columns = ref([
60
60
  {
61
61
  name: "last_modified",
62
62
  label: i18n.t("Last Modified"),
63
- field: "last_modified_human",
63
+ field: "lastModifiedHuman",
64
64
  align: "left",
65
65
  },
66
66
  {
67
67
  name: "size_display",
68
68
  label: i18n.t("Size"),
69
69
  field: "size",
70
- align: "left",
70
+ align: "right",
71
+ format: (val) => {
72
+ if (val == null) {
73
+ return "";
74
+ }
75
+ return humanStorageSize(val)
76
+ }
71
77
  },
72
78
  { name: "action" },
73
79
  ]);
@@ -126,7 +132,7 @@ const loadItems = async () => {
126
132
  filesParams.search = localSearch.value;
127
133
  }
128
134
 
129
- let files = await q("fsListFiles", filesParams, ["name", "size", "mime", "path"]);
135
+ let files = await q("fsListFiles", filesParams, ["name", "size", "mime", "path", "canPreview", "imagePath", "lastModified", "lastModifiedHuman"]);
130
136
  files = files.map((item) => {
131
137
  item.type = "file";
132
138
  return item;
@@ -189,7 +195,7 @@ const breadcrumbs = computed(() => {
189
195
  ps.push(p);
190
196
  breadcrumbs.push({
191
197
  label: p,
192
- path: "/" + ps.join("/"),
198
+ path: ps.join("/"),
193
199
  });
194
200
  }
195
201
  }
@@ -198,6 +204,7 @@ const breadcrumbs = computed(() => {
198
204
  });
199
205
 
200
206
  const onClickRow = (evt, row, index) => {
207
+ console.log("click row", row)
201
208
  if (row.type == "folder") {
202
209
  preview.value = null;
203
210
  return;
@@ -484,7 +491,7 @@ const isDark = computed(() => light.isDarkMode());
484
491
 
485
492
  <div>
486
493
  <q-input outlined :placeholder="$t('Search for file name')" dense @keyup.enter.native="submitSearch"
487
- v-model="search">
494
+ v-model="search" :color="$light.color">
488
495
  <template v-slot:append>
489
496
  <q-btn flat dense round icon="sym_o_search"></q-btn>
490
497
  </template>
@@ -492,7 +499,7 @@ const isDark = computed(() => light.isDarkMode());
492
499
  </div>
493
500
 
494
501
  <q-space></q-space>
495
- <q-btn v-if="closable" icon="close" flat round @click="$emit('close')"></q-btn>
502
+ <q-btn v-if="closeable" icon="close" flat round @click="$emit('close')"></q-btn>
496
503
  </q-toolbar>
497
504
  </q-header>
498
505
 
@@ -524,7 +531,7 @@ const isDark = computed(() => light.isDarkMode());
524
531
  </q-item-section>
525
532
  </q-item>
526
533
 
527
- <q-list>
534
+ <q-list dense>
528
535
  <q-item>
529
536
  <q-item-section avatar>
530
537
  <q-icon name="sym_o_storage" />
@@ -534,13 +541,9 @@ const isDark = computed(() => light.isDarkMode());
534
541
  <q-btn dense round flat icon="sym_o_refresh" @click="reloadStorage"></q-btn>
535
542
  </q-item-section>
536
543
  </q-item>
537
- <q-item>
538
- <q-item-section>
539
- <q-tree ref="folderTree" class="q-pl-md" :nodes="folders" node-key="path" label-key="name"
540
- @lazy-load="onLazyLoad" v-model:selected="path" no-selection-unset>
541
- </q-tree>
542
- </q-item-section>
543
- </q-item>
544
+ <q-tree ref="folderTree" class="q-pl-md" :nodes="folders" node-key="path" label-key="name"
545
+ @lazy-load="onLazyLoad" v-model:selected="path" no-selection-unset>
546
+ </q-tree>
544
547
  </q-list>
545
548
  <!-- q-expansion-item :label="$t('Storage')" default-expand-all default-opened icon="sym_o_storage" selectable
546
549
  @click="path = '/'">
@@ -557,7 +560,6 @@ const isDark = computed(() => light.isDarkMode());
557
560
  </q-drawer>
558
561
 
559
562
  <q-drawer side="right" show-if-above bordered>
560
-
561
563
  <l-file-manager-preview v-model="preview" v-if="preview" :key="preview.path" />
562
564
  </q-drawer>
563
565
 
@@ -566,19 +568,18 @@ const isDark = computed(() => light.isDarkMode());
566
568
  <q-dialog v-model="showUploadFiles" persistent transition-show="scale" transition-hide="scale">
567
569
  <l-card style="width:300px">
568
570
  <q-card-section>
569
-
570
- <q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files"></q-file>
571
+ <q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files" :color="light.color"></q-file>
571
572
  </q-card-section>
572
573
 
573
574
  <q-card-actions align="right">
574
- <q-btn flat label="Cancel" :color="light.color" v-close-popup></q-btn>
575
- <q-btn flat label="Upload" :color="light.color" @click="onUploadFiles"></q-btn>
575
+ <q-btn flat :label="$t('Cancel')" :color="light.color" v-close-popup></q-btn>
576
+ <q-btn flat :label="$t('Upload')" :color="light.color" @click="onUploadFiles"></q-btn>
576
577
  </q-card-actions>
577
578
  </l-card>
578
579
  </q-dialog>
579
580
 
580
581
  <q-toolbar>
581
- <q-breadcrumbs>
582
+ <q-breadcrumbs :active-color="$light.color">
582
583
  <q-breadcrumbs-el v-for="(b, index) in breadcrumbs" :label="b.label" :key="index" @click="path = b.path"
583
584
  href="javascript:void(0)"></q-breadcrumbs-el>
584
585
  </q-breadcrumbs>
@@ -605,10 +606,11 @@ const isDark = computed(() => light.isDarkMode());
605
606
  </q-toolbar>
606
607
 
607
608
  <template v-if="grid">
608
- <q-table title="Folders" flat bordered grid :columns="columns" :rows="foldersGrid">
609
+ <q-table :title="$t('Folders')" flat grid :columns="columns" :rows="foldersGrid" hide-pagination
610
+ :pagination="{ rowsPerPage: 0 }">
609
611
  <template v-slot:item="props">
610
- <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
611
- <q-card>
612
+ <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onDblclickRow(null, props.row, null)">
613
+ <q-card flat bordered>
612
614
  <q-item>
613
615
  <q-item-section avatar>
614
616
  <q-icon name="sym_o_folder" size="sm"></q-icon>
@@ -617,7 +619,7 @@ const isDark = computed(() => light.isDarkMode());
617
619
  {{ props.row.name }}
618
620
  </q-item-section>
619
621
  <q-item-section avatar>
620
- <q-checkbox v-model="selected" :val="props.row" />
622
+ <q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
621
623
  </q-item-section>
622
624
  </q-item>
623
625
  </q-card>
@@ -625,22 +627,26 @@ const isDark = computed(() => light.isDarkMode());
625
627
  </template>
626
628
  </q-table>
627
629
 
628
- <q-table title="Files" flat bordered grid :columns="columns" :rows="filesGrid" :pagination="pagination">
630
+ <q-table :title="$t('Files')" flat grid :columns="columns" :rows="filesGrid" hide-pagination
631
+ :pagination="{ rowsPerPage: 0 }">
629
632
 
630
633
  <template v-slot:item="props">
631
- <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
632
- <q-card>
634
+ <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onClickRow(null, props.row, null)">
635
+ <q-card flat bordered>
633
636
  <q-item>
634
637
  <q-item-section avatar>
635
638
  <q-icon name="sym_o_description" size="sm"></q-icon>
636
639
  </q-item-section>
637
- <q-item-section>
640
+ <q-item-section no-wrap>
638
641
  {{ props.row.name }}
639
642
  </q-item-section>
640
643
  <q-item-section avatar>
641
- <q-checkbox v-model="selected" :val="props.row" />
644
+ <q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
642
645
  </q-item-section>
643
646
  </q-item>
647
+
648
+ <q-img v-if="props.row.canPreview" :src="props.row.imagePath"></q-img>
649
+
644
650
  </q-card>
645
651
  </div>
646
652
  </template>
@@ -648,7 +654,7 @@ const isDark = computed(() => light.isDarkMode());
648
654
  </template>
649
655
  <template v-else>
650
656
 
651
- <q-table flat bordered :columns="columns" :rows="items" @row-dblclick="onDblclickRow" @row-click="onClickRow"
657
+ <q-table flat :columns="columns" :rows="items" @row-dblclick="onDblclickRow" @row-click="onClickRow"
652
658
  :pagination="pagination" row-key="path" selection="multiple" v-model:selected="selected" dense
653
659
  :loading="loading" :loading-label="$t('Loading...')" :no-data-label="$t('No data available')">
654
660
  <template #body-cell-icon="props">
@@ -4,7 +4,8 @@ import { ref, computed, useAttrs } from "vue";
4
4
  import { useLight, m, q } from '#imports';
5
5
  import { Loading, Dialog } from "quasar";
6
6
  import { useI18n } from "vue-i18n";
7
-
7
+ import { format } from 'quasar'
8
+ const { humanStorageSize } = format
8
9
  const light = useLight();
9
10
  const i18n = useI18n();
10
11
 
@@ -65,32 +66,6 @@ const attrs = computed(() => {
65
66
  });
66
67
 
67
68
 
68
- //@param bytes Number of bytes.
69
- //@param si True to use metric (SI) units, aka powers of 1000. False to use binary (IEC), aka powers of 1024.
70
- //@param dp Number of decimal places to display.
71
- const humanFileSize = (bytesSize, si = false, dp = 1) => {
72
- const thresh = si ? 1000 : 1024;
73
-
74
- if (Math.abs(bytesSize) < thresh) {
75
- return bytesSize + ' B';
76
- }
77
-
78
- const units = si
79
- ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
80
- : ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; //: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
81
- let u = -1;
82
- const r = 10 ** dp;
83
-
84
- do {
85
- bytesSize /= thresh;
86
- ++u;
87
- } while (Math.round(Math.abs(bytesSize) * r) / r >= thresh && u < units.length - 1);
88
-
89
- return bytesSize.toFixed(dp) + ' ' + units[u];
90
- }
91
-
92
-
93
-
94
69
  const uploadFile = ref<File | null>(null);
95
70
  const rename = ref(true);
96
71
 
@@ -109,7 +84,7 @@ const onUploadFile = async () => {
109
84
  if (uploadFile.value.size > system.maxUploadSize) {
110
85
  Dialog.create({
111
86
  title: "Error",
112
- message: "File size is too big. Max size is " + humanFileSize(system.maxUploadSize, false, 0),
87
+ message: "File size is too big. Max size is " + humanStorageSize(system.maxUploadSize),
113
88
  color: "negative"
114
89
  });
115
90
  return;
@@ -162,7 +137,7 @@ const onUploadFile = async () => {
162
137
  <l-card style="width:300px">
163
138
  <q-card-section>
164
139
  <q-file ref="file" v-model="uploadFile" name="file" :label="$t('File')" :accept="accept"
165
- :hint="`Max upload size: ${humanFileSize(system.maxUploadSize, false, 0)}`"></q-file>
140
+ :hint="`Max upload size: ${humanStorageSize(system.maxUploadSize)}`"></q-file>
166
141
  <!-- q-checkbox v-model="rename" :label="$t('Rename file if exists')"></q-checkbox-->
167
142
  </q-card-section>
168
143
 
@@ -118,6 +118,8 @@ const attrs = computed(() => {
118
118
  a.label = t(props.label);
119
119
  }
120
120
 
121
+ if (props.color === undefined) a.color = light.color
122
+
121
123
  return a;
122
124
 
123
125
  });
@@ -375,8 +375,8 @@ const getFilterValue = () => {
375
375
 
376
376
 
377
377
  const onFilters = () => {
378
- console.log("onFilters",filters.value)
379
-
378
+ console.log("onFilters", filters.value)
379
+
380
380
  //clone the filters
381
381
  onRequest({
382
382
  pagination: {
@@ -572,7 +572,8 @@ const localSelected = computed({
572
572
 
573
573
 
574
574
  <template #top-right="props" v-if="fullscreen || searchable">
575
- <q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')">
575
+ <q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
576
+ :color="$light.color">
576
577
  <template v-slot:append>
577
578
  <q-icon name="search" />
578
579
  </template>
@@ -15,7 +15,7 @@ const value = computed({
15
15
 
16
16
  </script>
17
17
  <template>
18
- <l-field label="Roles" stack-label :label="context.label" :error="error" :error-message="errorMessage">
18
+ <l-field stack-label :label="context.label" :error="error" :error-message="errorMessage">
19
19
  <q-option-group type="checkbox" :options="context.options" v-model="value" inline v-bind="context.attrs" :color="$light.color">
20
20
  </q-option-group>
21
21
  </l-field>
@@ -1,4 +1,8 @@
1
1
  {
2
+ "Menu width": "選單寬度",
3
+ "File system": "檔案系統",
4
+ "Folders": "資料夾",
5
+ "Files": "檔案",
2
6
  "Server time": "伺服器時間",
3
7
  "Developer": "開發者",
4
8
  "Modules": "模組",
@@ -1,8 +1,9 @@
1
1
  <script setup>
2
2
  import { reactive } from 'vue'
3
- import { m } from '../../'
4
-
3
+ import { m } from '#imports'
4
+ import { useI18n } from 'vue-i18n';
5
5
  import { query } from '@hostlink/light';
6
+ const { t } = useI18n();
6
7
 
7
8
  const { app, listRole: roles } = await query({
8
9
  app: {
@@ -15,7 +16,7 @@ const { app, listRole: roles } = await query({
15
16
  })
16
17
 
17
18
  const columns = [{
18
- label: "Permission",
19
+ label: t("Permission"),
19
20
  field: "permission",
20
21
  align: "left",
21
22
  }];
@@ -77,13 +78,9 @@ const onUpdate = (value, role, permission) => {
77
78
  </q-td>
78
79
  <q-td v-for="role in roles">
79
80
  <q-checkbox v-model="props.row[role.name]"
80
- @update:model-value="onUpdate($event, role.name, props.row.permission)"
81
-
82
- :color="$light.color"
83
- />
81
+ @update:model-value="onUpdate($event, role.name, props.row.permission)" :color="$light.color" />
84
82
  </q-td>
85
83
  </q-tr>
86
-
87
84
  </template>
88
85
  </q-table>
89
86
  </l-page>
@@ -2,7 +2,8 @@
2
2
  import { useQuasar } from 'quasar'
3
3
  import { q, m, notify } from '#imports'
4
4
  import { ref } from "vue"
5
-
5
+ import { useI18n } from 'vue-i18n';
6
+ const { t } = useI18n();
6
7
  const qua = useQuasar();
7
8
 
8
9
  const loadData = async () => {
@@ -40,7 +41,7 @@ const columns = [
40
41
  {
41
42
  name: "name",
42
43
  field: "name",
43
- label: "Name",
44
+ label: t("Name"),
44
45
  align: "left",
45
46
  sortable: true,
46
47
  }, {
@@ -124,8 +125,8 @@ const onRemoveUser = async (value, user) => {
124
125
 
125
126
  <template #body-cell-children="props">
126
127
  <q-td>
127
- <q-select :options="role_options" v-model="props.row.children" multiple use-chips
128
- dense @remove="onRemoveChild(props.row.name, $event)" @add="onAddChild(props.row.name, $event)">
128
+ <q-select :options="role_options" v-model="props.row.children" multiple use-chips dense
129
+ @remove="onRemoveChild(props.row.name, $event)" @add="onAddChild(props.row.name, $event)">
129
130
 
130
131
  </q-select>
131
132
  </q-td>
@@ -1,7 +1,14 @@
1
1
  <script setup>
2
2
  import { q } from "#imports"
3
+ import { Loading } from "quasar";
3
4
  const onClickDownload = async () => {
4
5
 
6
+ //show loading
7
+ Loading.show({
8
+ message: "Exporting database..."
9
+ });
10
+
11
+
5
12
  const data = await q({
6
13
  system: {
7
14
  database: {
@@ -10,6 +17,9 @@ const onClickDownload = async () => {
10
17
  }
11
18
  });
12
19
 
20
+ //hide loading
21
+ Loading.hide();
22
+
13
23
  const file = new File([data.system.database.export], "backup.sql", {
14
24
  type: "text/plain;charset=utf-8"
15
25
  });
@@ -24,6 +34,6 @@ const onClickDownload = async () => {
24
34
  </script>
25
35
  <template>
26
36
  <l-page>
27
- <q-btn color="primary" label="Download" icon="sym_o_download" @click="onClickDownload"></q-btn>
37
+ <l-btn label="Download" icon="sym_o_download" @click="onClickDownload"></l-btn>
28
38
  </l-page>
29
39
  </template>
@@ -14,12 +14,11 @@ const { system: { database } } = await query({
14
14
  <template>
15
15
  <l-page>
16
16
  <q-card flat bordered>
17
- <q-list bordered class="rounded-borders" separator>
18
- <q-expansion-item :label="table.name" v-for="table in database.table">
19
- <div class=" q-ma-sm">
20
- <q-table
21
- separator="cell"
22
- dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
17
+ <q-list class="rounded-borders" separator bordered>
18
+ <q-expansion-item :label="table.name" v-for="table in database.table" dense>
19
+ <div class="q-ma-sm">
20
+ <q-table separator="cell" dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination
21
+ flat bordered></q-table>
23
22
  </div>
24
23
 
25
24
  </q-expansion-item>
@@ -50,17 +50,11 @@ const columns = [
50
50
  field: row => row.type,
51
51
  format: val => `${val}`,
52
52
  sortable: true
53
- },
54
- {
55
- name: 'location',
56
- required: true,
57
- label: 'Location',
53
+ }, {
54
+ name: 'data',
55
+ label: 'Data',
58
56
  align: 'left',
59
- field: row => row.location,
60
- format: val => `${val}`,
61
- sortable: true
62
57
  }
63
-
64
58
  ]
65
59
  </script>
66
60
  <template>
@@ -79,25 +73,33 @@ const columns = [
79
73
  <FormKit type="l-input" label="Name" name="name" validation="required"></FormKit>
80
74
  <FormKit type="l-select" label="Type" name="type" validation="required" :options="[
81
75
  { label: 'Local', value: 'local' },
82
- { label: 'S3', value: 'S3' }
76
+ { label: 'S3', value: 'S3' },
77
+ { label: 'HostLink storage', value: 'hostlink' }
83
78
  ]"></FormKit>
84
79
 
85
- <template v-if="value.type == 'local'">
86
- <FormKit type="l-input" label="Location" name="location" validation="required"></FormKit>
87
- </template>
88
-
89
- <template v-if="value.type == 'S3'">
90
- <FormKit type="l-input" label="Bucket" name="bucket" validation="required"></FormKit>
91
- <FormKit type="l-input" label="Access Key" name="accessKey" validation="required"></FormKit>
92
- <FormKit type="l-input" label="Secret Key" name="secretKey" validation="required"></FormKit>
93
- <FormKit type="l-input" label="Region" name="region" validation="required"></FormKit>
94
- <FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
95
- <FormKit type="l-input" label="Prefix" name="prefix"></FormKit>
96
- <FormKit type="l-select" label="Visibility" name="visibility" validation="required" :options="[
97
- { label: 'Public', value: 'public' },
98
- { label: 'Private', value: 'private' }
99
- ]"></FormKit>
100
- </template>
80
+ <FormKit type="group" name="data">
81
+ <template v-if="value.type == 'local'">
82
+ <FormKit type="l-input" label="Location" name="location" validation="required"></FormKit>
83
+ </template>
84
+
85
+ <template v-if="value.type == 'S3'">
86
+ <FormKit type="l-input" label="Bucket" name="bucket" validation="required"></FormKit>
87
+ <FormKit type="l-input" label="Access Key" name="accessKey" validation="required"></FormKit>
88
+ <FormKit type="l-input" label="Secret Key" name="secretKey" validation="required"></FormKit>
89
+ <FormKit type="l-input" label="Region" name="region" validation="required"></FormKit>
90
+ <FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
91
+ <FormKit type="l-input" label="Prefix" name="prefix"></FormKit>
92
+ <FormKit type="l-select" label="Visibility" name="visibility" validation="required" :options="[
93
+ { label: 'Public', value: 'public' },
94
+ { label: 'Private', value: 'private' }
95
+ ]"></FormKit>
96
+ </template>
97
+
98
+ <template v-if="value.type == 'hostlink'">
99
+ <FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
100
+ <FormKit type="l-input" label="Token" name="token" validation="required"></FormKit>
101
+ </template>
102
+ </FormKit>
101
103
 
102
104
  </FormKit>
103
105
  </q-card>
@@ -114,6 +116,10 @@ const columns = [
114
116
 
115
117
  </template>
116
118
 
119
+ <template #body-cell-data="props">
120
+ <q-td><pre>{{ props.row.data }}</pre></q-td>
121
+ </template>
122
+
117
123
  </l-table>
118
124
  </l-page>
119
125
  </template>
@@ -106,13 +106,13 @@ const tab = ref('general')
106
106
  access_token_expire: obj.access_token_expire,
107
107
  }" v-if="tab == 'security'" @submit="onSubmit">
108
108
  <q-field label="Password policy" stack-label>
109
- <FormKit type="q-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
109
+ <FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
110
110
  false-value="0" />
111
- <FormKit type="q-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
111
+ <FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
112
112
  false-value="0" />
113
- <FormKit type="q-checkbox" label="Number" name="password_contains_numeric" true-value="1"
113
+ <FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1"
114
114
  false-value="0" />
115
- <FormKit type="q-checkbox" label="Special Character" name="password_contains_symbol"
115
+ <FormKit type="l-checkbox" label="Special Character" name="password_contains_symbol"
116
116
  true-value="1" false-value="0" />
117
117
  </q-field>
118
118
 
@@ -122,7 +122,7 @@ const tab = ref('general')
122
122
 
123
123
 
124
124
  <q-field label="Two factor authentication" stack-label>
125
- <FormKit type="q-checkbox" label="Enable" name="two_factor_authentication" true-value="1"
125
+ <FormKit type="l-checkbox" label="Enable" name="two_factor_authentication" true-value="1"
126
126
  false-value="0" />
127
127
  </q-field>
128
128
 
@@ -144,7 +144,7 @@ const tab = ref('general')
144
144
 
145
145
  }" v-if="tab == 'Modules'" @submit="onSubmit">
146
146
  <q-field label="File manager" stack-label>
147
- <FormKit type="q-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
147
+ <FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
148
148
  </q-field>
149
149
  </FormKit>
150
150
 
@@ -1,7 +1,9 @@
1
1
  <script setup>
2
2
  import { ref, reactive } from 'vue';
3
- import { m, q } from '../../'
3
+ import { m, q } from '#imports'
4
4
  import { Notify } from 'quasar';
5
+ import { useI18n } from 'vue-i18n';
6
+ const { t } = useI18n();
5
7
  const app = await q("app", ["languages"])
6
8
 
7
9
  const splitterModel = ref(62)
@@ -44,7 +46,7 @@ const columns = [
44
46
  name: "_delete"
45
47
  },
46
48
  {
47
- label: "Name",
49
+ label: t("Name"),
48
50
  name: "name",
49
51
  field: "name",
50
52
  align: "left",
@@ -98,8 +100,7 @@ const onDelete = async (name) => {
98
100
  <l-card>
99
101
  <q-splitter v-model="splitterModel" style="height:680px">
100
102
  <template #before>
101
- <q-table :rows="all" flat :rows-per-page-options="[0]" :columns="columns" dense
102
- separator="cell">
103
+ <q-table :rows="all" flat :rows-per-page-options="[0]" :columns="columns" dense separator="cell">
103
104
  <template #body="props">
104
105
  <q-tr :props="props">
105
106
  <q-td key="_delete" auto-width>
@@ -69,9 +69,10 @@ eventLogCols.forEach(col => {
69
69
 
70
70
  <q-separator />
71
71
  <l-list :bordered="false">
72
-
73
- <q-item-label header>Details</q-item-label>
72
+ <q-item-label header>{{ $t('Details') }}</q-item-label>
74
73
  <l-item label="Username">{{ my.username }}</l-item>
74
+ <l-item label="First name">{{ my.first_name }}</l-item>
75
+ <l-item label="Last name">{{ my.last_name }}</l-item>
75
76
  <l-item label="Email">{{ my.email }}</l-item>
76
77
  <l-item label="Phone">{{ my.phone }}</l-item>
77
78
  <l-item label="Address">{{ my.addr1 }} {{ my.addr2 }} {{ my.addr3 }}</l-item>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": "@hostlink/nuxt-light",
6
6
  "license": "MIT",