@hostlink/nuxt-light 1.0.1 → 1.0.3

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.
Files changed (39) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -13
  3. package/dist/runtime/components/l-app-main.vue +13 -4
  4. package/dist/runtime/components/l-card.vue +1 -1
  5. package/dist/runtime/components/l-col.vue +1 -1
  6. package/dist/runtime/components/l-customizer.vue +3 -4
  7. package/dist/runtime/components/l-file-manager.vue +4 -4
  8. package/dist/runtime/components/l-input-xlsx.vue +78 -0
  9. package/dist/runtime/components/l-login.vue +17 -9
  10. package/dist/runtime/components/l-page.vue +7 -0
  11. package/dist/runtime/components/l-row.vue +4 -2
  12. package/dist/runtime/components/l-table.vue +5 -4
  13. package/dist/runtime/formkit/File.vue +51 -0
  14. package/dist/runtime/formkit/Form.vue +2 -2
  15. package/dist/runtime/formkit/InputXlsx.vue +22 -0
  16. package/dist/runtime/formkit/Toggle.vue +18 -0
  17. package/dist/runtime/formkit/index.mjs +12 -0
  18. package/dist/runtime/lib/index.mjs +1 -1
  19. package/dist/runtime/locales/en.json +5 -12
  20. package/dist/runtime/locales/zh-hk.json +15 -2
  21. package/dist/runtime/pages/Permission/all.vue +1 -1
  22. package/dist/runtime/pages/Role/add.vue +1 -5
  23. package/dist/runtime/pages/Role/index.vue +21 -5
  24. package/dist/runtime/pages/System/database/backup.vue +25 -2
  25. package/dist/runtime/pages/System/database/table.vue +5 -3
  26. package/dist/runtime/pages/System/index.vue +3 -2
  27. package/dist/runtime/pages/System/menu/index.vue +6 -1
  28. package/dist/runtime/pages/System/package.vue +21 -2
  29. package/dist/runtime/pages/System/setting.vue +65 -9
  30. package/dist/runtime/pages/System/test.vue +1 -0
  31. package/dist/runtime/pages/System/view_as.vue +38 -11
  32. package/dist/runtime/pages/Translate/index.vue +5 -4
  33. package/dist/runtime/pages/User/_user_id/edit.vue +13 -12
  34. package/dist/runtime/pages/User/add.vue +18 -10
  35. package/dist/runtime/pages/User/setting/bio-auth.vue +4 -7
  36. package/dist/runtime/pages/User/setting/password.vue +29 -11
  37. package/dist/runtime/pages/User/setting/two-factor-auth.vue +13 -7
  38. package/dist/types.d.mts +2 -2
  39. package/package.json +2 -4
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { useQuasar } from 'quasar'
3
- import { q, m } from '../../'
4
- import { ref, reactive } from "vue"
3
+ import { q, m, notify } from '#imports'
4
+ import { ref } from "vue"
5
5
 
6
6
  const qua = useQuasar();
7
7
 
@@ -60,6 +60,10 @@ const onRemoveChild = async (value, child) => {
60
60
  child: child.value
61
61
 
62
62
  });
63
+
64
+ //notify
65
+ notify("Role removed from role")
66
+
63
67
  //refresh
64
68
  roles.value = await loadData();
65
69
  }
@@ -70,6 +74,10 @@ const onAddChild = async (value, child) => {
70
74
  child: child.value.value
71
75
 
72
76
  });
77
+
78
+ //notify
79
+ notify("Role added to role")
80
+
73
81
  //refresh
74
82
  roles.value = await loadData();
75
83
  }
@@ -80,6 +88,10 @@ const onAddUser = async (value, user) => {
80
88
  user_id: user.value.user_id
81
89
 
82
90
  });
91
+
92
+ //notify
93
+ notify("User added to role")
94
+
83
95
  //refresh
84
96
  roles.value = await loadData();
85
97
  }
@@ -90,6 +102,10 @@ const onRemoveUser = async (value, user) => {
90
102
  user_id: user.value.user_id
91
103
 
92
104
  });
105
+
106
+ //notify
107
+ notify("User removed from role")
108
+
93
109
  //refresh
94
110
  roles.value = await loadData();
95
111
  }
@@ -97,7 +113,7 @@ const onRemoveUser = async (value, user) => {
97
113
 
98
114
  <template>
99
115
  <l-page>
100
- <q-table :rows="roles" flat bordered :columns="columns" :rows-per-page-options="[0]">
116
+ <q-table :rows="roles" flat bordered :columns="columns" :rows-per-page-options="[0]" dense>
101
117
  <template #body-cell-_can_delete="props">
102
118
  <q-td auto-width>
103
119
  <q-btn v-if="props.row.canDelete" flat round dense icon="sym_o_delete"
@@ -108,7 +124,7 @@ const onRemoveUser = async (value, user) => {
108
124
 
109
125
  <template #body-cell-children="props">
110
126
  <q-td>
111
- <q-select :options="role_options" v-model="props.row.children" multiple use-chips
127
+ <q-select :options="role_options" v-model="props.row.children" multiple use-chips dense
112
128
  @remove="onRemoveChild(props.row.name, $event)" @add="onAddChild(props.row.name, $event)">
113
129
 
114
130
  </q-select>
@@ -116,7 +132,7 @@ const onRemoveUser = async (value, user) => {
116
132
  </template>
117
133
  <template #body-cell-user="props">
118
134
  <q-td>
119
- <q-select :options="users" v-model="props.row.user" multiple use-chips option-label="name"
135
+ <q-select :options="users" v-model="props.row.user" multiple use-chips dense option-label="name"
120
136
  option-value="user_id" @add="onAddUser(props.row.name, $event)"
121
137
  @remove="onRemoveUser(props.row.name, $event)">
122
138
 
@@ -1,6 +1,29 @@
1
+ <script setup>
2
+ import { q } from "#imports"
3
+ const onClickDownload = async () => {
4
+
5
+ const data = await q({
6
+ system: {
7
+ database: {
8
+ export: true
9
+ }
10
+ }
11
+ });
12
+
13
+ const file = new File([data.system.database.export], "backup.sql", {
14
+ type: "text/plain;charset=utf-8"
15
+ });
16
+ //save to local
17
+
18
+ const a = document.createElement("a");
19
+ a.download = file.name;
20
+ a.href = URL.createObjectURL(file);
21
+ a.click();
22
+ URL.revokeObjectURL(a.href);
23
+ }
24
+ </script>
1
25
  <template>
2
26
  <l-page>
3
- comming soon
4
-
27
+ <q-btn color="primary" label="Download" icon="sym_o_download" @click="onClickDownload"></q-btn>
5
28
  </l-page>
6
29
  </template>
@@ -14,10 +14,12 @@ 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" expand-separator>
17
+ <q-list bordered class="rounded-borders" separator>
18
+ <q-expansion-item :label="table.name" v-for="table in database.table">
19
19
  <div class=" q-ma-sm">
20
- <q-table :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
20
+ <q-table
21
+ separator="cell"
22
+ dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
21
23
  </div>
22
24
 
23
25
  </q-expansion-item>
@@ -5,7 +5,8 @@ const system = await q("system", ["server"])
5
5
  const columns = [
6
6
  {
7
7
  name: "name",
8
- label: "Name"
8
+ label: "Name",
9
+ sortable: true
9
10
  },
10
11
  {
11
12
  name: "value",
@@ -17,6 +18,6 @@ const columns = [
17
18
  </script>
18
19
  <template>
19
20
  <l-page>
20
- <l-table :rows="system.server" :columns="columns" :rows-per-page-options="[0]" hide-pagination></l-table>
21
+ <l-table searchable :rows="system.server" :columns="columns" :rows-per-page-options="[0]" hide-pagination></l-table>
21
22
  </l-page>
22
23
  </template>
@@ -274,10 +274,15 @@ const onAddChildMenu = (node, type) => {
274
274
  <div class="q-gutter-md">
275
275
  <l-input label="Label" v-model="selectedNode.label" />
276
276
  <l-input label="To" v-model="selectedNode.to" />
277
- <l-input label="Icon" v-model="selectedNode.icon" hint="example: sym_o_add"/>
277
+ <l-input label="Icon" v-model="selectedNode.icon" hint="example: sym_o_add" />
278
278
  <l-input label="Permission" v-model="selectedNode.permission" />
279
279
  <l-input label="UUID" v-model="selectedNode.uuid" readonly />
280
280
  </div>
281
+
282
+ <div>
283
+ <a href="https://fonts.google.com/icons" target="_blank">Material Icons</a>
284
+ </div>
285
+
281
286
  </q-card-section>
282
287
  </template>
283
288
 
@@ -1,9 +1,28 @@
1
1
  <script setup>
2
- import { q } from '../../'
2
+ import { ref, computed } from 'vue'
3
+ import { q } from '#imports'
3
4
  const { system } = await q({ system: ["package"] })
5
+ const filter = ref("")
6
+ const filtered = computed(() => {
7
+ if (!filter.value) return system.package
8
+ return system.package.filter((row) => {
9
+ return Object.values(row).some((val) => {
10
+ return String(val).toLowerCase().includes(filter.value.toLowerCase())
11
+ })
12
+ })
13
+ })
14
+
4
15
  </script>
5
16
  <template>
6
17
  <l-page>
7
- <q-table :rows="system.package" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
18
+ <q-table dense :rows="filtered" :rows-per-page-options="[0]" hide-pagination flat bordered separator="cell">
19
+ <template v-slot:top-right>
20
+ <q-input outlined dense debounce="300" v-model="filter" placeholder="Search">
21
+ <template v-slot:append>
22
+ <q-icon name="sym_o_search" />
23
+ </template>
24
+ </q-input>
25
+ </template>
26
+ </q-table>
8
27
  </l-page>
9
28
  </template>
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { Notify } from 'quasar'
3
+ import { reset } from "@formkit/core"
3
4
  import { useRouter } from 'vue-router'
4
5
  import { q, m } from '../../'
5
6
 
@@ -11,8 +12,20 @@ const obj = app.config.reduce((acc, cur) => {
11
12
  }, {});
12
13
 
13
14
  const fields = ["company", "company_logo",
14
- "password_upper_case", "password_lower_case", "password_number", "password_special_character",
15
- "file_manager", "two_factor_authentication"];
15
+ "password_contains_uppercase",
16
+ "password_contains_lowercase",
17
+ "password_contains_numeric",
18
+ "password_contains_symbol",
19
+ "password_min_length",
20
+ "file_manager",
21
+ "two_factor_authentication",
22
+ "mode",
23
+ "auth_lockout_duration",
24
+ "auth_lockout_attempts",
25
+ "access_token_expire",
26
+ "copyright_year",
27
+ "copyright_name"
28
+ ];
16
29
 
17
30
  //filter out fields that are not in the app.config table
18
31
  Object.keys(obj).forEach((key) => {
@@ -38,26 +51,40 @@ const onSubmit = async (d, form) => {
38
51
  icon: "check"
39
52
  })
40
53
 
41
- router.go(-1);
54
+ reset(form, d)
42
55
 
43
56
 
44
57
  }
58
+
59
+
60
+ if (obj.mode != 'prod') {
61
+ obj.mode = 'dev'
62
+ } else {
63
+ obj.mode = 'prod'
64
+ }
65
+
66
+
45
67
  </script>
46
68
  <template>
47
69
  <l-page>
48
-
49
70
  <FormKit type="l-form" :value="obj" #default="{ value }" @submit="onSubmit">
50
- <FormKit type="l-input" label="Company" name="company"></FormKit>
71
+ <FormKit type="l-input" label="Company" name="company" validation="required"></FormKit>
51
72
  <FormKit type="l-input" label="Company logo" name="company_logo"></FormKit>
52
73
 
74
+ <q-separator />
75
+
53
76
  <q-field label="Password policy" stack-label>
54
- <FormKit type="q-checkbox" label="Upper Case" name="password_upper_case" true-value="1" false-value="0" />
55
- <FormKit type="q-checkbox" label="Lower Case" name="password_lower_case" true-value="1" false-value="0" />
56
- <FormKit type="q-checkbox" label="Number" name="password_number" true-value="1" false-value="0" />
57
- <FormKit type="q-checkbox" label="Special Character" name="password_special_character" true-value="1"
77
+ <FormKit type="q-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
78
+ false-value="0" />
79
+ <FormKit type="q-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
80
+ false-value="0" />
81
+ <FormKit type="q-checkbox" label="Number" name="password_contains_numeric" true-value="1" false-value="0" />
82
+ <FormKit type="q-checkbox" label="Special Character" name="password_contains_symbol" true-value="1"
58
83
  false-value="0" />
59
84
  </q-field>
60
85
 
86
+ <FormKit type="l-input" label="Password min Length" name="password_min_length" validation="required|number">
87
+ </FormKit>
61
88
 
62
89
  <q-field label="File manager" stack-label>
63
90
  <FormKit type="q-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
@@ -67,7 +94,36 @@ const onSubmit = async (d, form) => {
67
94
  <FormKit type="q-checkbox" label="Enable" name="two_factor_authentication" true-value="1" false-value="0" />
68
95
  </q-field>
69
96
 
97
+ <FormKit label="Mode" type="l-select" :options="[
98
+ { label: 'Production', value: 'prod' },
99
+ { label: 'Development', value: 'dev' },
100
+
101
+ ]" name="mode" validation="required">
102
+ </FormKit>
103
+
104
+ <q-separator />
105
+
106
+ <FormKit label="Auth lockout duration" type="l-input" name="auth_lockout_duration"
107
+ hint="The number of minutes the user is locked out after the maximum number of failed login attempts. Default is 15 minutes." />
108
+
109
+ <FormKit label="Auth lockout attempts" type="l-input" name="auth_lockout_attempts"
110
+ hint="The number of failed login attempts before the user is locked out. Default is 5 attempts." />
111
+
112
+
113
+
114
+ <FormKit label="Access token expiration" type="l-input" name="access_token_expire"
115
+ hint="The access token expiration time in seconds. Default is 28800 seconds (8 hours)." />
116
+
117
+
118
+
119
+
120
+ <q-separator />
121
+
122
+ <FormKit label="Copyright name" type="l-input" name="copyright_name" />
123
+ <FormKit label="Copyright year" type="l-input" name="copyright_year" validation="required" />
124
+
70
125
 
71
126
  </FormKit>
127
+
72
128
  </l-page>
73
129
  </template>
@@ -13,6 +13,7 @@ const onSave = async () => {
13
13
  })
14
14
 
15
15
  }
16
+
16
17
  </script>
17
18
  <template>
18
19
  <l-page>
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
- import { list, m } from '../../'
2
+ import { list, m } from '#imports'
3
3
  import { useRouter } from "vue-router"
4
+ import { ref, computed } from "vue"
5
+ import { Dialog } from 'quasar'
4
6
 
5
7
  let { data: users } = await list("User", null, ["user_id", "username", "name", "roles"]);
6
8
 
@@ -31,26 +33,51 @@ let columns = [
31
33
 
32
34
  const router = useRouter();
33
35
  const onCickView = async (id) => {
36
+ try {
37
+ if (await m("viewAs", { user_id: id })) {
38
+ router.back();
39
+ }
40
+ } catch (e) {
41
+ Dialog.create({
42
+ title: "Error",
43
+ message: e.message,
44
+ color: "negative",
45
+ ok: true
46
+ })
34
47
 
35
- if (await m("viewAs", { user_id: id })) {
36
- router.back();
37
48
  }
38
-
39
49
  }
50
+
51
+ const filter = ref("")
52
+ const filtered = computed(() => {
53
+ if (!filter.value) return users
54
+ return users.filter((row) => {
55
+ return Object.values(row).some((val) => {
56
+ return String(val).toLowerCase().includes(filter.value.toLowerCase())
57
+ })
58
+ })
59
+ })
40
60
  </script>
41
61
  <template>
42
62
  <l-page>
43
- <q-table flat :columns="columns" :rows="users" :rows-per-page-options="[0]">
63
+ <p>
64
+ Use this page to view the system as another user. This is useful for testing permissions.
65
+ </p>
66
+
67
+ <q-table flat :columns="columns" :rows="filtered" :rows-per-page-options="[0]" dense>
68
+ <template v-slot:top-right>
69
+ <q-input outlined dense debounce="300" v-model="filter" placeholder="Search" clearable>
70
+ <template v-slot:append>
71
+ <q-icon name="sym_o_search" />
72
+ </template>
73
+ </q-input>
74
+ </template>
75
+
44
76
  <template #body-cell-view="props">
45
77
  <q-td :props="props">
46
- <q-btn rounded outline color="primary" @click="onCickView(props.row.user_id)" label="view"
47
- icon="sym_o_search"></q-btn>
78
+ <q-btn round circle flat dense @click="onCickView(props.row.user_id)" icon="sym_o_search"></q-btn>
48
79
  </q-td>
49
-
50
80
  </template>
51
-
52
81
  </q-table>
53
-
54
-
55
82
  </l-page>
56
83
  </template>
@@ -98,7 +98,8 @@ const onDelete = async (name) => {
98
98
  <l-card>
99
99
  <q-splitter v-model="splitterModel" style="height:680px">
100
100
  <template #before>
101
- <q-table :rows="all" flat hide-bottom :rows-per-page-options="[0]" :columns="columns">
101
+ <q-table :rows="all" flat :rows-per-page-options="[0]" :columns="columns" dense
102
+ separator="cell">
102
103
  <template #body="props">
103
104
  <q-tr :props="props">
104
105
  <q-td key="_delete" auto-width>
@@ -122,9 +123,9 @@ const onDelete = async (name) => {
122
123
  </template>
123
124
  <template #after>
124
125
  <l-form :bordered="false" @save="onSave">
125
- <l-input label="Name" required v-model.trim="obj.name"></l-input>
126
- <l-input v-for="language in app.languages" :label="language.name"
127
- v-model="obj[language.value]"></l-input>
126
+ <l-input label="Name" required v-model.trim="obj.name" clearable></l-input>
127
+ <l-input v-for="language in app.languages" :label="language.name" v-model="obj[language.value]"
128
+ clearable></l-input>
128
129
  </l-form>
129
130
  </template>
130
131
  </q-splitter>
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { reactive } from 'vue'
3
- import { getObject } from '../../../'
3
+ import { getObject, q } from '#imports'
4
4
  const obj = reactive(await getObject(["username", "first_name", "last_name", "email", "phone",
5
5
  "addr1", "addr2", "addr3", "join_date", "expiry_date", "status", "language", "default_page"
6
6
  ]))
@@ -10,11 +10,16 @@ const options = [
10
10
  { label: 'Inactive', value: 1 }
11
11
  ];
12
12
 
13
- const languages = [
14
- { label: 'English', value: 'en' },
15
- { label: '中文', value: 'zh-hk' }
16
- ]
13
+ const tt = await q({
14
+ app: ["languages"],
15
+ })
17
16
 
17
+ const languages = tt.app.languages.map((lang) => {
18
+ return {
19
+ label: lang.name,
20
+ value: lang.value,
21
+ };
22
+ })
18
23
 
19
24
  </script>
20
25
 
@@ -22,32 +27,28 @@ const languages = [
22
27
  <l-page>
23
28
  <FormKit type="l-form" :value="obj">
24
29
  <l-row>
25
- <l-col md="6" gutter="md">
30
+ <l-col md="6">
26
31
  <FormKit type="l-input" label="Username" name="username" validation="required" />
27
32
  <FormKit type="l-input" label="First name" name="first_name" validation="required" />
28
33
  <FormKit type="l-input" label="Last name" name="last_name" />
29
34
  <FormKit type="l-input" label="Email" name="email" validation="required|email" />
30
35
  </l-col>
31
36
 
32
- <l-col md="6" gutter="md">
37
+ <l-col md="6">
33
38
  <FormKit type="l-input" label="Phone" name="phone" />
34
39
  <FormKit type="l-input" label="Address1" name="addr1" />
35
40
  <FormKit type="l-input" label="Address2" name="addr2" />
36
41
  <FormKit type="l-input" label="Address3" name="addr3" />
37
42
  </l-col>
38
43
 
39
- <l-col gutter="md">
44
+ <l-col>
40
45
  <FormKit type="l-date-picker" label="Join date" name="join_date" validation="required" />
41
46
  <FormKit type="l-date-picker" label="Expiry date" name="expiry_date" />
42
47
  <FormKit type="l-select" label="Status" name="status" :options="options" validation="required" />
43
48
  <FormKit type="l-select" label="Language" name="language" :options="languages" validation="required" />
44
49
  <FormKit type="l-input" label="Default page" name="default_page" />
45
-
46
50
  </l-col>
47
51
  </l-row>
48
-
49
52
  </FormKit>
50
-
51
-
52
53
  </l-page>
53
54
  </template>
@@ -1,8 +1,7 @@
1
1
  <script setup>
2
2
  import { reactive } from "vue"
3
3
  import { useRouter } from "vue-router";
4
- import { model } from "@hostlink/light";
5
- import { q } from '../../'
4
+ import { q } from '#imports'
6
5
  const router = useRouter()
7
6
  const obj = reactive({
8
7
  username: null,
@@ -43,10 +42,19 @@ const options = [
43
42
  { label: 'Inactive', value: 1 }
44
43
  ];
45
44
 
46
- const languages = [
47
- { label: 'English', value: 'en' },
48
- { label: '中文', value: 'zh-hk' }
49
- ]
45
+
46
+ const tt = await q({
47
+ app: ["languages"],
48
+ })
49
+
50
+ const languages = tt.app.languages.map((lang) => {
51
+ return {
52
+ label: lang.name,
53
+ value: lang.value,
54
+ };
55
+ })
56
+
57
+
50
58
 
51
59
 
52
60
  </script>
@@ -54,23 +62,23 @@ const languages = [
54
62
  <l-page>
55
63
  <FormKit type="l-form" :value="obj">
56
64
  <l-row>
57
- <l-col md="6" gutter="md">
65
+ <l-col md="6">
58
66
  <FormKit type="l-input" label="Username" name="username" validation="required" />
59
67
  <FormKit type="l-input" label="Password" name="password" :validation="system.passwordPolicy"
60
- input-type="password" />
68
+ input-type="password" autocomplete="new-password" />
61
69
  <FormKit type="l-input" label="First name" name="first_name" validation="required" />
62
70
  <FormKit type="l-input" label="Last name" name="last_name" />
63
71
  <FormKit type="l-input" label="Email" name="email" validation="required|email" />
64
72
  </l-col>
65
73
 
66
- <l-col md="6" gutter="md">
74
+ <l-col md="6">
67
75
  <FormKit type="l-input" label="Phone" name="phone" />
68
76
  <FormKit type="l-input" label="Address1" name="addr1" />
69
77
  <FormKit type="l-input" label="Address2" name="addr2" />
70
78
  <FormKit type="l-input" label="Address3" name="addr3" />
71
79
  </l-col>
72
80
 
73
- <l-col gutter="md">
81
+ <l-col>
74
82
  <FormKit type="l-date-picker" label="Join date" name="join_date" validation="required" />
75
83
  <FormKit type="l-date-picker" label="Expiry date" name="expiry_date" />
76
84
  <FormKit type="l-select" label="Status" name="status" :options="options" validation="required" />
@@ -1,12 +1,11 @@
1
1
  <script setup>
2
2
  import { Dialog } from "quasar";
3
3
  import { ref } from "vue"
4
- import { q, m, getCurrentUser } from '../../../'
4
+ import { q, m, getCurrentUser } from '#imports'
5
5
  const app = await q("app", ["hasBioAuth"]);
6
6
 
7
7
  import { webauthnRegister } from "@hostlink/light"
8
8
 
9
-
10
9
  const data = ref(await q("listWebAuthn", ["uuid", "ip", "user_agent", "createdTime"]));
11
10
 
12
11
  const register = async () => {
@@ -16,14 +15,11 @@ const register = async () => {
16
15
  const user = await getCurrentUser();
17
16
  localStorage.setItem("username", user.username);
18
17
  } catch (e) {
19
-
20
18
  Dialog.create({
21
19
  title: "Error",
22
20
  message: e.message,
23
- ok: "OK",
21
+ ok: "OK"
24
22
  })
25
-
26
-
27
23
  }
28
24
  }
29
25
 
@@ -55,6 +51,7 @@ const deleteItem = async (uuid) => {
55
51
  //confirm
56
52
  Dialog.create({
57
53
  title: "Delete",
54
+ color: "negative",
58
55
  message: "Are you sure you want to delete this item?",
59
56
  ok: "Yes",
60
57
  cancel: "No",
@@ -78,7 +75,7 @@ const deleteItem = async (uuid) => {
78
75
  <l-btn label="Register" @click="register" icon="sym_o_add"></l-btn>
79
76
  </q-card-section>
80
77
 
81
- <q-table :rows="data" :columns="columns">
78
+ <q-table :rows="data" :columns="columns" dense :rows-per-page-options="[0]">
82
79
  <template #body-cell-action="props">
83
80
  <q-td :props="props" auto-width>
84
81
  <q-btn @click="deleteItem(props.row.uuid)" icon="sym_o_delete" round flat dense></q-btn>
@@ -1,11 +1,13 @@
1
1
  <script setup>
2
-
3
- import { Dialog } from 'quasar';
2
+ import { useI18n } from 'vue-i18n'
3
+ import { Dialog } from 'quasar'
4
4
  import { reset } from "@formkit/core"
5
5
  import { updatePassword } from "@hostlink/light"
6
-
6
+ import { computed } from 'vue'
7
7
  import { q } from "#imports"
8
8
 
9
+ const { t } = useI18n()
10
+
9
11
  const onSubmit = async (data, form) => {
10
12
  if (await updatePassword(data.old_password, data.new_password)) {
11
13
  reset(form);
@@ -21,25 +23,41 @@ const onSubmit = async (data, form) => {
21
23
  message: "Old password is incorrect",
22
24
  ok: "OK"
23
25
  })
24
-
25
-
26
26
  }
27
27
  }
28
28
 
29
29
  const system = await q("system", ["passwordPolicy"])
30
30
 
31
+ const policies = computed(() => {
32
+ return system.passwordPolicy.split("|").map((policy) => {
33
+ let name = policy.split(":")[0]
34
+
35
+ if (name == "length") {
36
+ return t('Must contain at least {0} characters', [policy.split(":")[1]]);
37
+ }
38
+
39
+ return t(name);
40
+ })
41
+ })
42
+
31
43
  </script>
32
44
  <template>
33
45
  <FormKit type="l-form" :bordered="false" @submit="onSubmit">
34
- <FormKit type="l-input" label="Old password" name="old_password" inputType="password" validation="required" />
46
+ <FormKit type="l-input" label="Old password" name="old_password" inputType="password" validation="required"
47
+ autocomplete="current-password" />
35
48
  <FormKit type="l-input" label="New password" name="new_password" inputType="password"
36
- :validation="system.passwordPolicy" />
49
+ :validation="system.passwordPolicy" autocomplete="new-password" />
37
50
  <FormKit type="l-input" label="Confirm password" name="new_password_confirm" inputType="password"
38
51
  validation="required|confirm" />
39
52
  </FormKit>
40
53
 
41
- <div>
42
- <div>Password policy</div>
43
- <div>{{ system.passwordPolicy }}</div>
44
- </div>
54
+ <q-card flat>
55
+ <q-card-section>
56
+ <div>{{ $t('Password policy') }}</div>
57
+ <ul>
58
+ <li v-for="policy in policies" :key="policy">{{ policy }}</li>
59
+ </ul>
60
+
61
+ </q-card-section>
62
+ </q-card>
45
63
  </template>