@hostlink/nuxt-light 1.7.1 → 1.8.0
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/runtime/components/L/Revision.vue +187 -0
- package/dist/runtime/components/l-login.vue +3 -1
- package/dist/runtime/pages/System/setting.vue +38 -29
- package/dist/runtime/pages/User/_user_id/view.vue +2 -3
- package/dist/runtime/pages/User/index.vue +1 -2
- package/dist/runtime/types/User.d.ts +5 -0
- package/dist/runtime/types/User.mjs +7 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Dialog, Notify } from "quasar"
|
|
3
|
+
import { computed, ref, reactive } from "vue";
|
|
4
|
+
import { q, m } from "#imports";
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
modelId: {
|
|
8
|
+
type: String,
|
|
9
|
+
required: true
|
|
10
|
+
},
|
|
11
|
+
modelClass: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const { revisionsByModel } = await q({
|
|
19
|
+
"revisionsByModel": {
|
|
20
|
+
__args: {
|
|
21
|
+
model_id: parseInt(props.modelId),
|
|
22
|
+
model_class: props.modelClass
|
|
23
|
+
},
|
|
24
|
+
revision_id: true,
|
|
25
|
+
revisionBy: true,
|
|
26
|
+
createdTime: true,
|
|
27
|
+
content: true,
|
|
28
|
+
delta: true
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const num_of_revisions = computed(() => {
|
|
34
|
+
return revisionsByModel.length;
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const showDialog = ref(false);
|
|
38
|
+
|
|
39
|
+
const selectedRevision = ref(null);
|
|
40
|
+
const onClick = (revision) => {
|
|
41
|
+
selectedRevision.value = revision;
|
|
42
|
+
|
|
43
|
+
showDialog.value = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const onRestore = () => {
|
|
47
|
+
//confirm restore
|
|
48
|
+
Dialog.create({
|
|
49
|
+
title: 'Restore',
|
|
50
|
+
message: 'Are you sure you want to restore selected fields?',
|
|
51
|
+
ok: 'Yes',
|
|
52
|
+
cancel: 'No'
|
|
53
|
+
}).onOk(async () => {
|
|
54
|
+
//restore
|
|
55
|
+
if (await m("restoreRevision", {
|
|
56
|
+
revision_id: selectedRevision.value.revision_id,
|
|
57
|
+
fields: restore_fields.value
|
|
58
|
+
})) {
|
|
59
|
+
Notify.create({
|
|
60
|
+
message: 'Restored successfully',
|
|
61
|
+
color: 'positive'
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
//reload
|
|
65
|
+
window.location.reload();
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const restore_fields = ref([]);
|
|
75
|
+
|
|
76
|
+
const onToggleAll = () => {
|
|
77
|
+
if (restore_fields.value.length === 0) {
|
|
78
|
+
restore_fields.value = Object.keys(getFilteredContent.value);
|
|
79
|
+
} else {
|
|
80
|
+
restore_fields.value = [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const isCheckedAll = computed(() => {
|
|
85
|
+
return restore_fields.value.length === Object.keys(getFilteredContent.value).length;
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const isAllowRestore = computed(() => {
|
|
89
|
+
return restore_fields.value.length > 0;
|
|
90
|
+
})
|
|
91
|
+
const showOnlyDelta = ref(false);
|
|
92
|
+
|
|
93
|
+
const getFilteredContent = computed(() => {
|
|
94
|
+
if (showOnlyDelta.value) {
|
|
95
|
+
const contents = {};
|
|
96
|
+
|
|
97
|
+
for (const key in selectedRevision.value.delta) {
|
|
98
|
+
contents[key] = selectedRevision.value.content[key];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return contents;
|
|
102
|
+
} else {
|
|
103
|
+
return selectedRevision.value.content;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
})
|
|
107
|
+
</script>
|
|
108
|
+
<template>
|
|
109
|
+
<div>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
<q-dialog v-model="showDialog" full-height full-width>
|
|
113
|
+
<q-card>
|
|
114
|
+
<q-toolbar>
|
|
115
|
+
<l-btn label="Restore" icon="sym_o_restore" permission="revision.restore" @click="onRestore"
|
|
116
|
+
:disabled="!isAllowRestore" />
|
|
117
|
+
|
|
118
|
+
<q-checkbox label="Show delta only" v-model="showOnlyDelta" :color="$light.color"></q-checkbox>
|
|
119
|
+
<q-space />
|
|
120
|
+
<q-btn flat round dense icon="close" @click="showDialog = false" />
|
|
121
|
+
</q-toolbar>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
<div class="q-pa-md">
|
|
125
|
+
<div class="text-h4 q-mb-md">
|
|
126
|
+
Revision
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<q-markup-table dense separator="cell" flat bordered>
|
|
130
|
+
<thead>
|
|
131
|
+
<tr>
|
|
132
|
+
<th width="48px">
|
|
133
|
+
<q-checkbox @click="onToggleAll" v-model="isCheckedAll"
|
|
134
|
+
:color="$light.color"></q-checkbox>
|
|
135
|
+
</th>
|
|
136
|
+
<th>Field</th>
|
|
137
|
+
<th>Value</th>
|
|
138
|
+
<th>Delta</th>
|
|
139
|
+
</tr>
|
|
140
|
+
</thead>
|
|
141
|
+
<tbody>
|
|
142
|
+
<tr v-for="(value, key) in getFilteredContent" :key="key">
|
|
143
|
+
|
|
144
|
+
<td><q-checkbox v-model="restore_fields" :val="key" :color="$light.color"></q-checkbox>
|
|
145
|
+
</td>
|
|
146
|
+
<td :class="{ 'bg-yellow-3': selectedRevision.delta[key] != undefined }">
|
|
147
|
+
{{ key }}</td>
|
|
148
|
+
<td>
|
|
149
|
+
<pre>{{ value }}</pre>
|
|
150
|
+
</td>
|
|
151
|
+
<td>
|
|
152
|
+
<pre>{{ selectedRevision.delta[key] }}</pre>
|
|
153
|
+
</td>
|
|
154
|
+
</tr>
|
|
155
|
+
</tbody>
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
</q-markup-table>
|
|
159
|
+
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
</q-card>
|
|
165
|
+
</q-dialog>
|
|
166
|
+
|
|
167
|
+
<l-card>
|
|
168
|
+
<q-list>
|
|
169
|
+
<q-expansion-item :label="`Revisions: ${num_of_revisions}`" icon="sym_o_history" expand-separator>
|
|
170
|
+
|
|
171
|
+
<q-list>
|
|
172
|
+
<q-item v-for="revision in revisionsByModel" :key="revision.revision_id"
|
|
173
|
+
@click="onClick(revision)" clickable>
|
|
174
|
+
<q-item-section>
|
|
175
|
+
<q-item-label>Revision by: {{ revision.revisionBy }}</q-item-label>
|
|
176
|
+
<q-item-label caption>{{ revision.createdTime }}</q-item-label>
|
|
177
|
+
|
|
178
|
+
</q-item-section>
|
|
179
|
+
</q-item>
|
|
180
|
+
</q-list>
|
|
181
|
+
|
|
182
|
+
</q-expansion-item>
|
|
183
|
+
|
|
184
|
+
</q-list>
|
|
185
|
+
</l-card>
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
@@ -157,8 +157,10 @@ onMounted(() => {
|
|
|
157
157
|
google.accounts.id.initialize({
|
|
158
158
|
client_id: props.googleClientId,
|
|
159
159
|
callback: handleGoogleCredentialResponse,
|
|
160
|
-
|
|
160
|
+
use_fedcm_for_prompt: true
|
|
161
161
|
|
|
162
|
+
});
|
|
163
|
+
google.accounts.id.prompt();
|
|
162
164
|
google.accounts.id.renderButton(
|
|
163
165
|
document.getElementById('g_id_signin'),
|
|
164
166
|
{
|
|
@@ -23,9 +23,12 @@ const fields = ["company", "company_logo",
|
|
|
23
23
|
"auth_lockout_attempts",
|
|
24
24
|
"access_token_expire",
|
|
25
25
|
"copyright_year",
|
|
26
|
-
"copyright_name"
|
|
26
|
+
"copyright_name",
|
|
27
|
+
"revision"
|
|
27
28
|
];
|
|
28
29
|
|
|
30
|
+
obj.revision = obj.revision ? obj.revision.split(',') : []
|
|
31
|
+
|
|
29
32
|
//filter out fields that are not in the app.config table
|
|
30
33
|
Object.keys(obj).forEach((key) => {
|
|
31
34
|
if (!fields.includes(key)) {
|
|
@@ -80,11 +83,11 @@ const tab = ref('general')
|
|
|
80
83
|
</template>
|
|
81
84
|
<template #after>
|
|
82
85
|
<FormKit type="l-form" :bordered="false" :value="{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
company: obj.company,
|
|
87
|
+
company_logo: obj.company_logo,
|
|
88
|
+
copyright_name: obj.copyright_name,
|
|
89
|
+
copyright_year: obj.copyright_year
|
|
90
|
+
}" v-if="tab == 'general'" @submit="onSubmit">
|
|
88
91
|
<FormKit type="l-input" label="Company" name="company" validation="required"></FormKit>
|
|
89
92
|
<FormKit type="l-input" label="Company logo" name="company_logo"></FormKit>
|
|
90
93
|
|
|
@@ -94,21 +97,21 @@ const tab = ref('general')
|
|
|
94
97
|
</FormKit>
|
|
95
98
|
|
|
96
99
|
<FormKit type="l-form" :bordered="false" :value="{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
password_contains_uppercase: obj.password_contains_uppercase,
|
|
101
|
+
password_contains_lowercase: obj.password_contains_lowercase,
|
|
102
|
+
password_contains_numeric: obj.password_contains_numeric,
|
|
103
|
+
password_contains_symbol: obj.password_contains_symbol,
|
|
104
|
+
password_min_length: obj.password_min_length,
|
|
105
|
+
two_factor_authentication: obj.two_factor_authentication,
|
|
106
|
+
auth_lockout_duration: obj.auth_lockout_duration,
|
|
107
|
+
auth_lockout_attempts: obj.auth_lockout_attempts,
|
|
108
|
+
access_token_expire: obj.access_token_expire,
|
|
109
|
+
}" v-if="tab == 'security'" @submit="onSubmit">
|
|
107
110
|
<q-field label="Password policy" stack-label>
|
|
108
|
-
<FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase"
|
|
109
|
-
false-value="0" />
|
|
110
|
-
<FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase"
|
|
111
|
-
false-value="0" />
|
|
111
|
+
<FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase"
|
|
112
|
+
true-value="1" false-value="0" />
|
|
113
|
+
<FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase"
|
|
114
|
+
true-value="1" false-value="0" />
|
|
112
115
|
<FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1"
|
|
113
116
|
false-value="0" />
|
|
114
117
|
<FormKit type="l-checkbox" label="Special Character" name="password_contains_symbol"
|
|
@@ -139,23 +142,29 @@ const tab = ref('general')
|
|
|
139
142
|
</FormKit>
|
|
140
143
|
|
|
141
144
|
<FormKit type="l-form" :bordered="false" :value="{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
file_manager: obj.file_manager,
|
|
146
|
+
revision: obj.revision
|
|
147
|
+
}" v-if="tab == 'Modules'" @submit="onSubmit">
|
|
145
148
|
<q-field label="File manager" stack-label>
|
|
146
|
-
<FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1"
|
|
149
|
+
<FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1"
|
|
150
|
+
false-value="0" />
|
|
147
151
|
</q-field>
|
|
152
|
+
|
|
153
|
+
<FormKit type="q-select" label="Revision" name="revision" use-input use-chips multiple
|
|
154
|
+
hide-dropdown-icon input-debounce="0" new-value-mode="add-unique" />
|
|
155
|
+
|
|
156
|
+
|
|
148
157
|
</FormKit>
|
|
149
158
|
|
|
150
159
|
<FormKit type="l-form" :bordered="false" :value="{
|
|
151
|
-
|
|
160
|
+
mode: obj.mode,
|
|
152
161
|
|
|
153
|
-
|
|
162
|
+
}" v-if="tab == 'Developer'" @submit="onSubmit">
|
|
154
163
|
<FormKit label="Mode" type="l-select" :options="[
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
{ label: 'Production', value: 'prod' },
|
|
165
|
+
{ label: 'Development', value: 'dev' },
|
|
157
166
|
|
|
158
|
-
|
|
167
|
+
]" name="mode" validation="required">
|
|
159
168
|
</FormKit>
|
|
160
169
|
</FormKit>
|
|
161
170
|
</template>
|
|
@@ -7,18 +7,17 @@ const route = useRoute();
|
|
|
7
7
|
const light = useLight();
|
|
8
8
|
|
|
9
9
|
const tab = ref('overview');
|
|
10
|
-
const splitter = ref(10);
|
|
11
10
|
const id = route.params.user_id;
|
|
12
11
|
</script>
|
|
13
12
|
|
|
14
13
|
<template>
|
|
15
14
|
<l-page edit-btn>
|
|
16
15
|
<template #header>
|
|
17
|
-
<l-btn to="change-password" icon="sym_o_key" permission="user.changePassword"
|
|
16
|
+
<l-btn to="change-password" icon="sym_o_key" permission="user.changePassword"
|
|
17
|
+
label="Change password"></l-btn>
|
|
18
18
|
<l-btn to="update-role" icon="sym_o_people" permission="user.role.add" label="Update role"></l-btn>
|
|
19
19
|
</template>
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
<q-card flat bordered>
|
|
23
22
|
<q-tabs v-model="tab" :active-color="$light.color" inline-label align="justify">
|
|
24
23
|
<q-tab name="overview" icon="sym_o_person" label="Overview" />
|
|
@@ -5,7 +5,7 @@ const onRequest = async (request) => {
|
|
|
5
5
|
request.loadObjects("User", { status: status.value });
|
|
6
6
|
//request.loadObjects("User");
|
|
7
7
|
};
|
|
8
|
-
const columns = model("User").columns(["username", "first_name", "label_name", "email", "phone", "join_date", "status"]);
|
|
8
|
+
const columns = model("User").columns(["username", "first_name", "label_name", "email", "phone", "join_date", "status","has2FA"]);
|
|
9
9
|
const status = ref("0");
|
|
10
10
|
const selected = ref([]);
|
|
11
11
|
</script>
|
|
@@ -13,7 +13,6 @@ const selected = ref([]);
|
|
|
13
13
|
<template>
|
|
14
14
|
<l-page>
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
<l-tabs v-model="status">
|
|
18
17
|
<l-tab label="Active" name="0">
|
|
19
18
|
<l-table row-key="user_id" @request="onRequest" :columns="columns"
|