@fishawack/lab-velocity 2.0.0-beta.43 → 2.0.0-beta.45
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/README.md +25 -0
- package/_Build/vue/components/layout/PageHeader.vue +7 -6
- package/_Build/vue/components/layout/Table.vue +5 -0
- package/_Build/vue/components/layout/TableSorter.vue +1 -0
- package/_Build/vue/components/layout/TokenDisplay.vue +52 -0
- package/_Build/vue/modules/AuthModule/js/guest-request.js +32 -0
- package/_Build/vue/modules/AuthModule/js/impersonation-banner.js +102 -0
- package/_Build/vue/modules/AuthModule/js/router.js +61 -27
- package/_Build/vue/modules/AuthModule/js/store.js +8 -0
- package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +2 -3
- package/_Build/vue/modules/AuthModule/routes/PIntegrations/columns.js +58 -0
- package/_Build/vue/modules/AuthModule/routes/PIntegrations/resource.js +53 -96
- package/_Build/vue/modules/AuthModule/routes/PTeams/columns.js +78 -0
- package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +206 -290
- package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordAction.vue +51 -0
- package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordDialog.vue +138 -0
- package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +39 -2
- package/_Build/vue/modules/AuthModule/routes/change-password.vue +6 -9
- package/_Build/vue/modules/AuthModule/routes/force-reset.vue +6 -9
- package/_Build/vue/modules/AuthModule/routes/forgot.vue +6 -1
- package/_Build/vue/modules/AuthModule/routes/login.vue +6 -9
- package/_Build/vue/modules/AuthModule/routes/loginsso.vue +7 -1
- package/_Build/vue/modules/AuthModule/routes/logout.vue +10 -2
- package/_Build/vue/modules/AuthModule/routes/register.vue +6 -8
- package/_Build/vue/modules/AuthModule/routes/reset.vue +6 -1
- package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +6 -1
- package/_Build/vue/modules/resource/index.js +9 -1
- package/_Build/vue/modules/resource/trashable.js +104 -0
- package/components/_table.scss +3 -0
- package/components/_token-display.scss +41 -0
- package/general.scss +1 -0
- package/index.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<vel-button @click="visible = true">Set Password</vel-button>
|
|
3
|
+
|
|
4
|
+
<SetPasswordDialog
|
|
5
|
+
v-if="visible"
|
|
6
|
+
:user-id="userId"
|
|
7
|
+
@close="visible = false"
|
|
8
|
+
@success="onSuccess"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import VelButton from "../../../../components/basic/Button.vue";
|
|
14
|
+
import SetPasswordDialog from "./SetPasswordDialog.vue";
|
|
15
|
+
import { ElNotification } from "element-plus";
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
components: {
|
|
19
|
+
VelButton,
|
|
20
|
+
SetPasswordDialog,
|
|
21
|
+
},
|
|
22
|
+
props: {
|
|
23
|
+
userId: {
|
|
24
|
+
type: [Number, String],
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
model: {
|
|
28
|
+
type: Object,
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
data() {
|
|
33
|
+
return {
|
|
34
|
+
visible: false,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
methods: {
|
|
38
|
+
onSuccess(data) {
|
|
39
|
+
this.visible = false;
|
|
40
|
+
|
|
41
|
+
Object.assign(this.model, data);
|
|
42
|
+
|
|
43
|
+
ElNotification({
|
|
44
|
+
title: "Success",
|
|
45
|
+
message: "Password updated",
|
|
46
|
+
type: "success",
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
</script>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-dialog
|
|
3
|
+
:model-value="true"
|
|
4
|
+
title="Set Password"
|
|
5
|
+
width="500px"
|
|
6
|
+
@close="$emit('close')"
|
|
7
|
+
>
|
|
8
|
+
<vel-basic
|
|
9
|
+
v-model="password"
|
|
10
|
+
name="password"
|
|
11
|
+
type="password"
|
|
12
|
+
label="Password"
|
|
13
|
+
placeholder="Enter new password"
|
|
14
|
+
class="AM-mb-2"
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
<vel-basic
|
|
18
|
+
v-model="passwordConfirmation"
|
|
19
|
+
name="password_confirmation"
|
|
20
|
+
type="password"
|
|
21
|
+
label="Confirm Password"
|
|
22
|
+
placeholder="Confirm new password"
|
|
23
|
+
class="AM-mb-2"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<div class="AM-mb-2">
|
|
27
|
+
<p class="font-700 AM-mb-0.5">Password must contain:</p>
|
|
28
|
+
<p class="m-0">
|
|
29
|
+
{{ passwordLengthValid ? "✓" : "✗" }} At least 8 characters
|
|
30
|
+
</p>
|
|
31
|
+
<p class="m-0">{{ hasLetter ? "✓" : "✗" }} At least one letter</p>
|
|
32
|
+
<p class="m-0">
|
|
33
|
+
{{ hasNumberOrSymbol ? "✓" : "✗" }} At least one number or
|
|
34
|
+
symbol
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<template #footer>
|
|
39
|
+
<el-button @click="$emit('close')">Cancel</el-button>
|
|
40
|
+
<el-button
|
|
41
|
+
type="primary"
|
|
42
|
+
:disabled="!canSubmit"
|
|
43
|
+
:loading="loading"
|
|
44
|
+
@click="submit"
|
|
45
|
+
>
|
|
46
|
+
Set Password
|
|
47
|
+
</el-button>
|
|
48
|
+
</template>
|
|
49
|
+
</el-dialog>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script>
|
|
53
|
+
import { ElDialog, ElButton, ElNotification } from "element-plus";
|
|
54
|
+
import VelBasic from "../../../../components/form/basic.vue";
|
|
55
|
+
import axios from "axios";
|
|
56
|
+
|
|
57
|
+
export default {
|
|
58
|
+
components: {
|
|
59
|
+
ElDialog,
|
|
60
|
+
ElButton,
|
|
61
|
+
VelBasic,
|
|
62
|
+
},
|
|
63
|
+
props: {
|
|
64
|
+
userId: {
|
|
65
|
+
type: [Number, String],
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
emits: ["close", "success"],
|
|
70
|
+
data() {
|
|
71
|
+
return {
|
|
72
|
+
password: "",
|
|
73
|
+
passwordConfirmation: "",
|
|
74
|
+
loading: false,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
computed: {
|
|
78
|
+
passwordLengthValid() {
|
|
79
|
+
return this.password.length >= 8;
|
|
80
|
+
},
|
|
81
|
+
hasLetter() {
|
|
82
|
+
return /[a-zA-Z]/.test(this.password);
|
|
83
|
+
},
|
|
84
|
+
hasNumberOrSymbol() {
|
|
85
|
+
return (
|
|
86
|
+
/[0-9]/.test(this.password) ||
|
|
87
|
+
/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(this.password)
|
|
88
|
+
);
|
|
89
|
+
},
|
|
90
|
+
passwordValid() {
|
|
91
|
+
return (
|
|
92
|
+
this.passwordLengthValid &&
|
|
93
|
+
this.hasLetter &&
|
|
94
|
+
this.hasNumberOrSymbol
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
canSubmit() {
|
|
98
|
+
return (
|
|
99
|
+
this.passwordValid &&
|
|
100
|
+
this.password === this.passwordConfirmation &&
|
|
101
|
+
this.password.length > 0
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
methods: {
|
|
106
|
+
async submit() {
|
|
107
|
+
this.loading = true;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const res = await axios.post(
|
|
111
|
+
`/api/users/${this.userId}/set-password`,
|
|
112
|
+
{
|
|
113
|
+
password: this.password,
|
|
114
|
+
password_confirmation: this.passwordConfirmation,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
this.$emit("success", res.data.data);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
if (e.response?.status === 422) {
|
|
121
|
+
const errors = e.response.data.errors || {};
|
|
122
|
+
const message =
|
|
123
|
+
Object.values(errors).flat().join("\n") ||
|
|
124
|
+
e.response.data.message;
|
|
125
|
+
|
|
126
|
+
ElNotification({
|
|
127
|
+
title: "Validation Error",
|
|
128
|
+
message,
|
|
129
|
+
type: "error",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
this.loading = false;
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
</script>
|
|
@@ -9,6 +9,7 @@ import usersColumns, { detectedCompany } from "./columns.js";
|
|
|
9
9
|
import VelFormRole from "../../../../components/layout/FormRole.vue";
|
|
10
10
|
import VelButton from "../../../../components/basic/Button.vue";
|
|
11
11
|
import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
|
|
12
|
+
import SetPasswordAction from "./SetPasswordAction.vue";
|
|
12
13
|
|
|
13
14
|
function generatePassword(
|
|
14
15
|
length = 20,
|
|
@@ -26,9 +27,8 @@ export default [
|
|
|
26
27
|
{
|
|
27
28
|
api: {
|
|
28
29
|
params: {
|
|
29
|
-
index: (
|
|
30
|
+
index: () => ({
|
|
30
31
|
include: "company",
|
|
31
|
-
"filter[withTrashed]": $route.query.trashed,
|
|
32
32
|
}),
|
|
33
33
|
show: () => ({ include: "company" }),
|
|
34
34
|
},
|
|
@@ -37,6 +37,7 @@ export default [
|
|
|
37
37
|
value: "email",
|
|
38
38
|
},
|
|
39
39
|
auditable: true,
|
|
40
|
+
trashable: true,
|
|
40
41
|
permissions: {
|
|
41
42
|
create: ({ $store }) => $store.getters.can("write users"),
|
|
42
43
|
edit: ({ $store }) => $store.getters.can("write users"),
|
|
@@ -152,6 +153,42 @@ export default [
|
|
|
152
153
|
},
|
|
153
154
|
show: {
|
|
154
155
|
actions: [
|
|
156
|
+
({ model, $store }) =>
|
|
157
|
+
$store.getters.can("write users") &&
|
|
158
|
+
!model.email_verified_at &&
|
|
159
|
+
h(
|
|
160
|
+
VelButton,
|
|
161
|
+
{
|
|
162
|
+
async onClick() {
|
|
163
|
+
try {
|
|
164
|
+
const res = await axios.post(
|
|
165
|
+
`/api/users/${model.id}/verify`,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
Object.assign(model, res.data.data);
|
|
169
|
+
|
|
170
|
+
ElNotification({
|
|
171
|
+
title: "Success",
|
|
172
|
+
message: "User marked as verified",
|
|
173
|
+
type: "success",
|
|
174
|
+
});
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.log(e);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
"Mark as Verified",
|
|
181
|
+
),
|
|
182
|
+
({ model, $store }) => {
|
|
183
|
+
if (!$store.getters.can("write users")) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return h(SetPasswordAction, {
|
|
188
|
+
userId: model.id,
|
|
189
|
+
model,
|
|
190
|
+
});
|
|
191
|
+
},
|
|
155
192
|
({ model, $store, $root }) =>
|
|
156
193
|
$store.getters.can("impersonate users") &&
|
|
157
194
|
h(
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
|
|
82
82
|
<script>
|
|
83
83
|
import Form from "form-backend-validation";
|
|
84
|
+
import { guestRequest } from "../js/guest-request";
|
|
84
85
|
|
|
85
86
|
export default {
|
|
86
87
|
components: {
|
|
@@ -126,15 +127,11 @@ export default {
|
|
|
126
127
|
this.loading = true;
|
|
127
128
|
|
|
128
129
|
try {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} catch (e) {}
|
|
135
|
-
|
|
136
|
-
await this.form.post("/login");
|
|
137
|
-
}
|
|
130
|
+
await guestRequest({
|
|
131
|
+
form: this.form,
|
|
132
|
+
url: "/login",
|
|
133
|
+
store: this.$store,
|
|
134
|
+
});
|
|
138
135
|
} catch (e) {
|
|
139
136
|
console.log(e);
|
|
140
137
|
} finally {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
<script>
|
|
51
51
|
import Form from "form-backend-validation";
|
|
52
52
|
import { ElNotification } from "element-plus";
|
|
53
|
+
import { guestRequest } from "../js/guest-request";
|
|
53
54
|
|
|
54
55
|
export default {
|
|
55
56
|
components: {
|
|
@@ -98,15 +99,11 @@ export default {
|
|
|
98
99
|
},
|
|
99
100
|
async login() {
|
|
100
101
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} catch (e) {}
|
|
107
|
-
|
|
108
|
-
await this.form.post("/login");
|
|
109
|
-
}
|
|
102
|
+
await guestRequest({
|
|
103
|
+
form: this.form,
|
|
104
|
+
url: "/login",
|
|
105
|
+
store: this.$store,
|
|
106
|
+
});
|
|
110
107
|
} catch (e) {
|
|
111
108
|
console.log(e);
|
|
112
109
|
} finally {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
<script>
|
|
49
49
|
import Form from "form-backend-validation";
|
|
50
|
+
import { guestRequest } from "../js/guest-request";
|
|
50
51
|
|
|
51
52
|
export default {
|
|
52
53
|
components: {
|
|
@@ -66,7 +67,11 @@ export default {
|
|
|
66
67
|
methods: {
|
|
67
68
|
async onSubmit() {
|
|
68
69
|
try {
|
|
69
|
-
await
|
|
70
|
+
await guestRequest({
|
|
71
|
+
form: this.form,
|
|
72
|
+
url: "/forgot-password",
|
|
73
|
+
store: this.$store,
|
|
74
|
+
});
|
|
70
75
|
|
|
71
76
|
this.$router.push({
|
|
72
77
|
name: "auth.success-forgot",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
|
|
66
66
|
<script>
|
|
67
67
|
import Form from "form-backend-validation";
|
|
68
|
+
import { guestRequest } from "../js/guest-request";
|
|
68
69
|
|
|
69
70
|
export default {
|
|
70
71
|
components: {
|
|
@@ -93,15 +94,11 @@ export default {
|
|
|
93
94
|
this.loading = true;
|
|
94
95
|
|
|
95
96
|
try {
|
|
96
|
-
const res = await
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
} catch (e) {}
|
|
102
|
-
|
|
103
|
-
await this.form.post("/login");
|
|
104
|
-
}
|
|
97
|
+
const res = await guestRequest({
|
|
98
|
+
form: this.form,
|
|
99
|
+
url: "/login",
|
|
100
|
+
store: this.$store,
|
|
101
|
+
});
|
|
105
102
|
|
|
106
103
|
await this.postLogin();
|
|
107
104
|
} catch (e) {
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
<script>
|
|
59
59
|
import Form from "form-backend-validation";
|
|
60
|
+
import { guestRequest } from "../js/guest-request";
|
|
60
61
|
|
|
61
62
|
export default {
|
|
62
63
|
components: {
|
|
@@ -87,7 +88,12 @@ export default {
|
|
|
87
88
|
this.loading = true;
|
|
88
89
|
|
|
89
90
|
try {
|
|
90
|
-
const res = await
|
|
91
|
+
const res = await guestRequest({
|
|
92
|
+
form: this.form,
|
|
93
|
+
url: "/hydrate/sso/check",
|
|
94
|
+
store: this.$store,
|
|
95
|
+
});
|
|
96
|
+
|
|
91
97
|
if (res["redirect_url"]) {
|
|
92
98
|
this.redirect_url = res["redirect_url"];
|
|
93
99
|
this.setRedirect();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
2
4
|
export default {
|
|
3
5
|
metaInfo() {
|
|
4
6
|
return {
|
|
@@ -11,9 +13,15 @@ export default {
|
|
|
11
13
|
await this.$store.dispatch("logout");
|
|
12
14
|
} catch {
|
|
13
15
|
/* empty */
|
|
14
|
-
} finally {
|
|
15
|
-
this.$router.push({ name: "auth.login" });
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await axios.get("/sanctum/csrf-cookie");
|
|
20
|
+
} catch {
|
|
21
|
+
/* empty */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.$router.push({ name: "auth.login" });
|
|
17
25
|
},
|
|
18
26
|
};
|
|
19
27
|
</script>
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
<script>
|
|
85
85
|
import Form from "form-backend-validation";
|
|
86
86
|
import { ElNotification } from "element-plus";
|
|
87
|
+
import { guestRequest } from "../js/guest-request";
|
|
87
88
|
|
|
88
89
|
export default {
|
|
89
90
|
components: {
|
|
@@ -116,15 +117,12 @@ export default {
|
|
|
116
117
|
this.loading = true;
|
|
117
118
|
|
|
118
119
|
try {
|
|
119
|
-
const res = await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
} catch (e) {}
|
|
120
|
+
const res = await guestRequest({
|
|
121
|
+
form: this.form,
|
|
122
|
+
url: "/register",
|
|
123
|
+
store: this.$store,
|
|
124
|
+
});
|
|
125
125
|
|
|
126
|
-
await this.form.post("/register");
|
|
127
|
-
}
|
|
128
126
|
if (res["redirect"]) {
|
|
129
127
|
// Redirect here
|
|
130
128
|
this.$router.push({
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
<script>
|
|
68
68
|
import Form from "form-backend-validation";
|
|
69
|
+
import { guestRequest } from "../js/guest-request";
|
|
69
70
|
|
|
70
71
|
export default {
|
|
71
72
|
components: {
|
|
@@ -98,7 +99,11 @@ export default {
|
|
|
98
99
|
methods: {
|
|
99
100
|
async onSubmit() {
|
|
100
101
|
try {
|
|
101
|
-
|
|
102
|
+
await guestRequest({
|
|
103
|
+
form: this.form,
|
|
104
|
+
url: "/reset-password",
|
|
105
|
+
store: this.$store,
|
|
106
|
+
});
|
|
102
107
|
|
|
103
108
|
this.$router.push({
|
|
104
109
|
name: "auth.success-reset",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
<script>
|
|
54
54
|
import Form from "form-backend-validation";
|
|
55
55
|
import { ElNotification } from "element-plus";
|
|
56
|
+
import { guestRequest } from "../js/guest-request";
|
|
56
57
|
|
|
57
58
|
export default {
|
|
58
59
|
components: {
|
|
@@ -77,7 +78,11 @@ export default {
|
|
|
77
78
|
this.notification = null;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
await
|
|
81
|
+
await guestRequest({
|
|
82
|
+
form: this.form,
|
|
83
|
+
url: "/forgot-password",
|
|
84
|
+
store: this.$store,
|
|
85
|
+
});
|
|
81
86
|
|
|
82
87
|
ElNotification({
|
|
83
88
|
type: "success",
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "element-plus";
|
|
14
14
|
import VelButton from "../../components/basic/Button.vue";
|
|
15
15
|
import VelAudit from "../../components/layout/Audit.vue";
|
|
16
|
+
import { applyTrashable } from "./trashable.js";
|
|
16
17
|
|
|
17
18
|
export const defaultResource = meta();
|
|
18
19
|
|
|
@@ -20,7 +21,7 @@ export function meta(name = "default", properties = {}) {
|
|
|
20
21
|
const singular = properties.singular || name.slice(0, -1);
|
|
21
22
|
const slug = properties.slug || kebabCase(name);
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
const result = merge(
|
|
24
25
|
{
|
|
25
26
|
name,
|
|
26
27
|
title: properties.title || name[0].toUpperCase() + name.slice(1),
|
|
@@ -52,6 +53,7 @@ export function meta(name = "default", properties = {}) {
|
|
|
52
53
|
label: `Search ${name}`,
|
|
53
54
|
},
|
|
54
55
|
auditable: false,
|
|
56
|
+
trashable: false,
|
|
55
57
|
form: {
|
|
56
58
|
component: null,
|
|
57
59
|
submit: null,
|
|
@@ -393,6 +395,12 @@ export function meta(name = "default", properties = {}) {
|
|
|
393
395
|
},
|
|
394
396
|
properties,
|
|
395
397
|
);
|
|
398
|
+
|
|
399
|
+
if (result.trashable) {
|
|
400
|
+
applyTrashable(result);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return result;
|
|
396
404
|
}
|
|
397
405
|
|
|
398
406
|
export function columns(columns = []) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { h } from "vue";
|
|
3
|
+
|
|
4
|
+
import VelTableSorter from "../../components/layout/TableSorter.vue";
|
|
5
|
+
import { ElNotification } from "element-plus";
|
|
6
|
+
import VelButton from "../../components/basic/Button.vue";
|
|
7
|
+
import VelCheckbox from "../../components/form/Checkbox.vue";
|
|
8
|
+
|
|
9
|
+
export function applyTrashable(result) {
|
|
10
|
+
const originalStructure = result.index.structure;
|
|
11
|
+
result.index.structure = (props) => {
|
|
12
|
+
const structure = originalStructure(props);
|
|
13
|
+
structure["json-data"].rowClassName = ({ row }) =>
|
|
14
|
+
row.deleted_at ? "el-table__row--trashed" : "";
|
|
15
|
+
return structure;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
result.table.actions.push(({ model, resource }, props) => {
|
|
19
|
+
const { $emit } = props;
|
|
20
|
+
|
|
21
|
+
if (resource.permissions.delete(props, { model }) && model.deleted_at) {
|
|
22
|
+
return h({
|
|
23
|
+
data: () => ({
|
|
24
|
+
loading: false,
|
|
25
|
+
}),
|
|
26
|
+
render() {
|
|
27
|
+
return h(
|
|
28
|
+
VelButton,
|
|
29
|
+
{
|
|
30
|
+
tag: "a",
|
|
31
|
+
type: "success",
|
|
32
|
+
size: "small",
|
|
33
|
+
loading: this.loading,
|
|
34
|
+
onClick: async () => {
|
|
35
|
+
this.loading = true;
|
|
36
|
+
|
|
37
|
+
await axios.post(
|
|
38
|
+
`${resource.api.endpoint(props)}/${model.id}/restore`,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
$emit("reload");
|
|
42
|
+
|
|
43
|
+
ElNotification({
|
|
44
|
+
title: "Success",
|
|
45
|
+
message: `${resource.singularTitle} restored.`,
|
|
46
|
+
type: "success",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.loading = false;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
() => "Restore",
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const originalLayout = [...result.index.layout];
|
|
60
|
+
result.index.layout = [
|
|
61
|
+
(props) => {
|
|
62
|
+
const { resource } = props;
|
|
63
|
+
|
|
64
|
+
return h({
|
|
65
|
+
data: () => ({
|
|
66
|
+
showTrashed: false,
|
|
67
|
+
}),
|
|
68
|
+
methods: {
|
|
69
|
+
reload() {
|
|
70
|
+
this.$refs.table?.reload();
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
render() {
|
|
74
|
+
const structureProps = resource.index.structure(props);
|
|
75
|
+
|
|
76
|
+
if (this.showTrashed) {
|
|
77
|
+
structureProps.apiParams = {
|
|
78
|
+
...structureProps.apiParams,
|
|
79
|
+
"filter[withTrashed]": 1,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return h("div", [
|
|
84
|
+
resource.permissions.delete(props) &&
|
|
85
|
+
h(VelCheckbox, {
|
|
86
|
+
label: `Show trashed ${resource.name}`,
|
|
87
|
+
modelValue: this.showTrashed,
|
|
88
|
+
class: "mt-3",
|
|
89
|
+
"onUpdate:modelValue": (val) => {
|
|
90
|
+
this.showTrashed = val;
|
|
91
|
+
this.$nextTick(() => this.reload());
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
h(VelTableSorter, {
|
|
95
|
+
...structureProps,
|
|
96
|
+
ref: "table",
|
|
97
|
+
}),
|
|
98
|
+
]);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
...originalLayout.slice(1),
|
|
103
|
+
];
|
|
104
|
+
}
|
package/components/_table.scss
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.token-display {
|
|
2
|
+
&__warning {
|
|
3
|
+
font-size: 14px;
|
|
4
|
+
color: #606266;
|
|
5
|
+
margin: 0 0 16px;
|
|
6
|
+
line-height: 1.6;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&__block {
|
|
10
|
+
margin-top: 16px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&__header {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
margin-bottom: 8px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__label {
|
|
21
|
+
font-size: 11px;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
letter-spacing: 0.08em;
|
|
24
|
+
text-transform: uppercase;
|
|
25
|
+
color: #909399;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&__value {
|
|
29
|
+
background: #1a1a2e;
|
|
30
|
+
color: #a8ff78;
|
|
31
|
+
border-radius: 6px;
|
|
32
|
+
padding: 16px 20px;
|
|
33
|
+
font-size: 13px;
|
|
34
|
+
line-height: 1.7;
|
|
35
|
+
overflow-x: auto;
|
|
36
|
+
white-space: pre-wrap;
|
|
37
|
+
word-break: break-all;
|
|
38
|
+
font-family: monospace;
|
|
39
|
+
margin: 0;
|
|
40
|
+
}
|
|
41
|
+
}
|