@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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -13
- package/dist/runtime/components/l-app-main.vue +13 -4
- package/dist/runtime/components/l-card.vue +1 -1
- package/dist/runtime/components/l-col.vue +1 -1
- package/dist/runtime/components/l-customizer.vue +3 -4
- package/dist/runtime/components/l-file-manager.vue +4 -4
- package/dist/runtime/components/l-input-xlsx.vue +78 -0
- package/dist/runtime/components/l-login.vue +17 -9
- package/dist/runtime/components/l-page.vue +7 -0
- package/dist/runtime/components/l-row.vue +4 -2
- package/dist/runtime/components/l-table.vue +5 -4
- package/dist/runtime/formkit/File.vue +51 -0
- package/dist/runtime/formkit/Form.vue +2 -2
- package/dist/runtime/formkit/InputXlsx.vue +22 -0
- package/dist/runtime/formkit/Toggle.vue +18 -0
- package/dist/runtime/formkit/index.mjs +12 -0
- package/dist/runtime/lib/index.mjs +1 -1
- package/dist/runtime/locales/en.json +5 -12
- package/dist/runtime/locales/zh-hk.json +15 -2
- package/dist/runtime/pages/Permission/all.vue +1 -1
- package/dist/runtime/pages/Role/add.vue +1 -5
- package/dist/runtime/pages/Role/index.vue +21 -5
- package/dist/runtime/pages/System/database/backup.vue +25 -2
- package/dist/runtime/pages/System/database/table.vue +5 -3
- package/dist/runtime/pages/System/index.vue +3 -2
- package/dist/runtime/pages/System/menu/index.vue +6 -1
- package/dist/runtime/pages/System/package.vue +21 -2
- package/dist/runtime/pages/System/setting.vue +65 -9
- package/dist/runtime/pages/System/test.vue +1 -0
- package/dist/runtime/pages/System/view_as.vue +38 -11
- package/dist/runtime/pages/Translate/index.vue +5 -4
- package/dist/runtime/pages/User/_user_id/edit.vue +13 -12
- package/dist/runtime/pages/User/add.vue +18 -10
- package/dist/runtime/pages/User/setting/bio-auth.vue +4 -7
- package/dist/runtime/pages/User/setting/password.vue +29 -11
- package/dist/runtime/pages/User/setting/two-factor-auth.vue +13 -7
- package/dist/types.d.mts +2 -2
- 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
|
|
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
|
-
|
|
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
|
|
18
|
-
<q-expansion-item :label="table.name" v-for="table in database.table"
|
|
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
|
|
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 {
|
|
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="
|
|
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
|
-
"
|
|
15
|
-
"
|
|
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
|
-
|
|
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="
|
|
55
|
-
|
|
56
|
-
<FormKit type="q-checkbox" label="
|
|
57
|
-
|
|
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>
|
|
@@ -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
|
-
<
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
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"
|
|
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"
|
|
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
|
|
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 {
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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"
|
|
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"
|
|
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
|
|
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
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
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>
|