devextreme-cli 1.3.0-beta.0 → 1.3.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/index.js +1 -1
- package/package.json +32 -9
- package/src/application.js +1 -1
- package/src/applications/application.angular.js +46 -10
- package/src/applications/application.react.js +6 -4
- package/src/applications/application.vue.js +102 -35
- package/src/commands.json +34 -25
- package/src/templates/react/application/src/App.js +3 -3
- package/src/templates/react/application/src/Content.js +1 -1
- package/src/templates/react/application/src/{NotAuthenticatedContent.js → UnauthenticatedContent.js} +1 -1
- package/src/templates/react/application/src/api/auth.js +1 -1
- package/src/templates/react/application/src/app-info.js +4 -2
- package/src/templates/react/application/src/components/change-password-form/change-password-form.js +1 -1
- package/src/templates/react/application/src/components/create-account-form/create-account-form.js +1 -1
- package/src/templates/react/application/src/components/footer/footer.js +2 -2
- package/src/templates/react/application/src/components/header/header.js +37 -37
- package/src/templates/react/application/src/components/login-form/login-form.js +1 -1
- package/src/templates/react/application/src/components/reset-password-form/reset-password-form.js +2 -2
- package/src/templates/react/application/src/components/side-navigation-menu/side-navigation-menu.js +5 -5
- package/src/templates/react/application/src/components/user-panel/user-panel.js +8 -2
- package/src/templates/react/application/src/dx-styles.scss +6 -1
- package/src/templates/react/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.js +1 -1
- package/src/templates/react/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.js +1 -1
- package/src/templates/react/application/src/layouts/single-card/single-card.js +11 -10
- package/src/templates/react/application/src/utils/media-query.js +1 -1
- package/src/templates/react/application/src/utils/patches.scss +1 -1
- package/src/templates/react/sample-pages/home/home.js +47 -46
- package/src/templates/react/sample-pages/profile/profile.js +3 -3
- package/src/templates/react/sample-pages/tasks/tasks.js +70 -68
- package/src/templates/vue-v2/application/devextreme.json +38 -0
- package/src/templates/{vue → vue-v2}/application/src/App.vue +3 -3
- package/src/templates/{vue → vue-v2}/application/src/app-info.js +0 -0
- package/src/templates/{vue → vue-v2}/application/src/app-navigation.js +0 -0
- package/src/templates/{vue → vue-v2}/application/src/auth.js +0 -0
- package/src/templates/{vue/application/src/components/the-footer.vue → vue-v2/application/src/components/app-footer.vue} +0 -0
- package/src/templates/{vue → vue-v2}/application/src/components/header-toolbar.vue +8 -1
- package/src/templates/{vue → vue-v2}/application/src/components/side-nav-menu.vue +0 -0
- package/src/templates/{vue → vue-v2}/application/src/components/user-panel.vue +0 -0
- package/src/templates/{vue → vue-v2}/application/src/dx-styles.scss +4 -0
- package/src/templates/{vue → vue-v2}/application/src/layouts/side-nav-inner-toolbar.vue +0 -0
- package/src/templates/{vue → vue-v2}/application/src/layouts/side-nav-outer-toolbar.vue +0 -0
- package/src/templates/{vue → vue-v2}/application/src/layouts/single-card.vue +0 -0
- package/src/templates/{vue → vue-v2}/application/src/main.js +0 -0
- package/src/templates/{vue → vue-v2}/application/src/router.js +3 -3
- package/src/templates/{vue → vue-v2}/application/src/themes/metadata.additional.json +0 -0
- package/src/templates/{vue → vue-v2}/application/src/themes/metadata.base.json +0 -0
- package/src/templates/{vue → vue-v2}/application/src/utils/media-query.js +0 -0
- package/src/templates/{vue → vue-v2}/application/src/views/change-password-form.vue +2 -3
- package/src/templates/{vue → vue-v2}/application/src/views/create-account-form.vue +2 -3
- package/src/templates/{vue → vue-v2}/application/src/views/login-form.vue +2 -3
- package/src/templates/{vue → vue-v2}/application/src/views/reset-password-form.vue +2 -3
- package/src/templates/{vue → vue-v2}/application/vue.config.js +0 -0
- package/src/templates/{vue → vue-v2}/page/page.vue +0 -0
- package/src/templates/{vue/sample-pages/home.vue → vue-v2/sample-pages/home-page.vue} +5 -5
- package/src/templates/{vue/sample-pages/profile.vue → vue-v2/sample-pages/profile-page.vue} +1 -1
- package/src/templates/{vue/sample-pages/tasks.vue → vue-v2/sample-pages/tasks-page.vue} +1 -0
- package/src/templates/{vue → vue-v3}/application/devextreme.json +3 -0
- package/src/templates/vue-v3/application/src/App.vue +101 -0
- package/src/templates/vue-v3/application/src/app-info.js +3 -0
- package/src/templates/vue-v3/application/src/app-navigation.js +21 -0
- package/src/templates/vue-v3/application/src/auth.js +101 -0
- package/src/templates/vue-v3/application/src/components/app-footer.vue +21 -0
- package/src/templates/vue-v3/application/src/components/header-toolbar.vue +161 -0
- package/src/templates/vue-v3/application/src/components/side-nav-menu.vue +204 -0
- package/src/templates/vue-v3/application/src/components/user-panel.vue +114 -0
- package/src/templates/vue-v3/application/src/dx-styles.scss +57 -0
- package/src/templates/vue-v3/application/src/layouts/side-nav-inner-toolbar.vue +192 -0
- package/src/templates/vue-v3/application/src/layouts/side-nav-outer-toolbar.vue +144 -0
- package/src/templates/vue-v3/application/src/layouts/single-card.vue +83 -0
- package/src/templates/vue-v3/application/src/main.js +10 -0
- package/src/templates/vue-v3/application/src/router.js +130 -0
- package/src/templates/vue-v3/application/src/themes/metadata.additional.json +12 -0
- package/src/templates/vue-v3/application/src/themes/metadata.base.json +7 -0
- package/src/templates/vue-v3/application/src/utils/media-query.js +33 -0
- package/src/templates/vue-v3/application/src/views/change-password-form.vue +115 -0
- package/src/templates/vue-v3/application/src/views/create-account-form.vue +155 -0
- package/src/templates/vue-v3/application/src/views/login-form.vue +146 -0
- package/src/templates/vue-v3/application/src/views/reset-password-form.vue +117 -0
- package/src/templates/vue-v3/application/vue.config.js +3 -0
- package/src/templates/vue-v3/page/page.vue +13 -0
- package/src/templates/vue-v3/sample-pages/home-page.vue +173 -0
- package/src/templates/vue-v3/sample-pages/profile-page.vue +88 -0
- package/src/templates/vue-v3/sample-pages/tasks-page.vue +133 -0
- package/src/themebuider.js +42 -63
- package/src/utility/latest-versions.js +3 -3
- package/src/utility/package-manager.js +8 -2
- package/src/{layout.js → utility/prompts/layout.js} +4 -4
- package/src/utility/prompts/prompts.js +16 -0
- package/src/utility/prompts/vue-version.js +29 -0
- package/src/utility/run-command.js +8 -6
- package/src/utility/prompts.js +0 -11
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<dx-scroll-view height="100%" width="100%" class="with-footer single-card">
|
|
3
|
+
<div class="dx-card content">
|
|
4
|
+
<div class="header">
|
|
5
|
+
<div class="title">{{title}}</div>
|
|
6
|
+
<div class="description">{{description}}</div>
|
|
7
|
+
</div>
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
</dx-scroll-view>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
import DxScrollView from "devextreme-vue/scroll-view";
|
|
15
|
+
|
|
16
|
+
import { useRoute } from 'vue-router';
|
|
17
|
+
import { watch, ref } from 'vue';
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
components: {
|
|
21
|
+
DxScrollView
|
|
22
|
+
},
|
|
23
|
+
setup() {
|
|
24
|
+
const route = useRoute();
|
|
25
|
+
|
|
26
|
+
const title = ref(route.meta.title);
|
|
27
|
+
const description = ref("");
|
|
28
|
+
|
|
29
|
+
watch(() => route.path,
|
|
30
|
+
() => {
|
|
31
|
+
title.value = route.meta.title;
|
|
32
|
+
description.value = route.meta.description;
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
return {
|
|
36
|
+
title,
|
|
37
|
+
description
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<style lang="scss">
|
|
44
|
+
@import "../themes/generated/variables.base.scss";
|
|
45
|
+
|
|
46
|
+
.single-card {
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
|
|
50
|
+
.dx-card {
|
|
51
|
+
width: 330px;
|
|
52
|
+
margin: auto auto;
|
|
53
|
+
padding: 40px;
|
|
54
|
+
flex-grow: 0;
|
|
55
|
+
|
|
56
|
+
.screen-x-small & {
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: 100%;
|
|
59
|
+
border-radius: 0;
|
|
60
|
+
box-shadow: none;
|
|
61
|
+
margin: 0;
|
|
62
|
+
border: 0;
|
|
63
|
+
flex-grow: 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.header {
|
|
67
|
+
margin-bottom: 30px;
|
|
68
|
+
|
|
69
|
+
.title {
|
|
70
|
+
color: $base-text-color;
|
|
71
|
+
line-height: 28px;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
font-size: 24px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.description {
|
|
77
|
+
color: rgba($base-text-color, alpha($base-text-color) * 0.7);
|
|
78
|
+
line-height: 18px;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import auth from "./auth";
|
|
2
|
+
import { createRouter, createWebHashHistory } from "vue-router";
|
|
3
|
+
|
|
4
|
+
<%=^empty%>import Home from "./views/home-page";
|
|
5
|
+
import Profile from "./views/profile-page";
|
|
6
|
+
import Tasks from "./views/tasks-page";
|
|
7
|
+
<%=/empty%>import defaultLayout from "./layouts/<%=layout%>";
|
|
8
|
+
import simpleLayout from "./layouts/single-card";
|
|
9
|
+
|
|
10
|
+
function loadView(view) {
|
|
11
|
+
return () => import (/* webpackChunkName: "login" */ `./views/${view}.vue`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const router = new createRouter({
|
|
15
|
+
routes: [<%=#empty%>
|
|
16
|
+
{
|
|
17
|
+
path: "/",
|
|
18
|
+
meta: {
|
|
19
|
+
layout: defaultLayout
|
|
20
|
+
}
|
|
21
|
+
},<%=/empty%><%=^empty%>
|
|
22
|
+
{
|
|
23
|
+
path: "/home",
|
|
24
|
+
name: "home",
|
|
25
|
+
meta: {
|
|
26
|
+
requiresAuth: true,
|
|
27
|
+
layout: defaultLayout
|
|
28
|
+
},
|
|
29
|
+
component: Home
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: "/profile",
|
|
33
|
+
name: "profile",
|
|
34
|
+
meta: {
|
|
35
|
+
requiresAuth: true,
|
|
36
|
+
layout: defaultLayout
|
|
37
|
+
},
|
|
38
|
+
component: Profile
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: "/tasks",
|
|
42
|
+
name: "tasks",
|
|
43
|
+
meta: {
|
|
44
|
+
requiresAuth: true,
|
|
45
|
+
layout: defaultLayout
|
|
46
|
+
},
|
|
47
|
+
component: Tasks
|
|
48
|
+
},<%=/empty%>
|
|
49
|
+
{
|
|
50
|
+
path: "/login-form",
|
|
51
|
+
name: "login-form",
|
|
52
|
+
meta: {
|
|
53
|
+
requiresAuth: false,
|
|
54
|
+
layout: simpleLayout,
|
|
55
|
+
title: "Sign In"
|
|
56
|
+
},
|
|
57
|
+
component: loadView("login-form")
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: "/reset-password",
|
|
61
|
+
name: "reset-password",
|
|
62
|
+
meta: {
|
|
63
|
+
requiresAuth: false,
|
|
64
|
+
layout: simpleLayout,
|
|
65
|
+
title: "Reset Password",
|
|
66
|
+
description: "Please enter the email address that you used to register, and we will send you a link to reset your password via Email."
|
|
67
|
+
},
|
|
68
|
+
component: loadView("reset-password-form")
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
path: "/create-account",
|
|
72
|
+
name: "create-account",
|
|
73
|
+
meta: {
|
|
74
|
+
requiresAuth: false,
|
|
75
|
+
layout: simpleLayout,
|
|
76
|
+
title: "Sign Up"
|
|
77
|
+
},
|
|
78
|
+
component: loadView("create-account-form"),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
path: "/change-password/:recoveryCode",
|
|
82
|
+
name: "change-password",
|
|
83
|
+
meta: {
|
|
84
|
+
requiresAuth: false,
|
|
85
|
+
layout: simpleLayout,
|
|
86
|
+
title: "Change Password"
|
|
87
|
+
},
|
|
88
|
+
component: loadView("change-password-form")
|
|
89
|
+
},<%=^empty%>
|
|
90
|
+
{
|
|
91
|
+
path: "/",
|
|
92
|
+
redirect: "/home"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
path: "/recovery",
|
|
96
|
+
redirect: "/home"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
path: "/:pathMatch(.*)*",
|
|
100
|
+
redirect: "/home"
|
|
101
|
+
}<%=/empty%><%=#empty%>
|
|
102
|
+
{
|
|
103
|
+
path: "/:pathMatch(.*)*",
|
|
104
|
+
redirect: "/"
|
|
105
|
+
}<%=/empty%>
|
|
106
|
+
],
|
|
107
|
+
history: createWebHashHistory()
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
router.beforeEach((to, from, next) => {
|
|
111
|
+
|
|
112
|
+
if (to.name === "login-form" && auth.loggedIn()) {
|
|
113
|
+
next({ name: "home" });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (to.matched.some(record => record.meta.requiresAuth)) {
|
|
117
|
+
if (!auth.loggedIn()) {
|
|
118
|
+
next({
|
|
119
|
+
name: "login-form",
|
|
120
|
+
query: { redirect: to.fullPath }
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
next();
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
next();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export default router;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const Breakpoints = {
|
|
2
|
+
XSmall: "(max-width: 599.99px)",
|
|
3
|
+
Small: "(min-width: 600px) and (max-width: 959.99px)",
|
|
4
|
+
Medium: "(min-width: 960px) and (max-width: 1279.99px)",
|
|
5
|
+
Large: "(min-width: 1280px)"
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
let handlers = [];
|
|
9
|
+
const xSmallMedia = window.matchMedia(Breakpoints.XSmall);
|
|
10
|
+
const smallMedia = window.matchMedia(Breakpoints.Small);
|
|
11
|
+
const mediumMedia = window.matchMedia(Breakpoints.Medium);
|
|
12
|
+
const largeMedia = window.matchMedia(Breakpoints.Large);
|
|
13
|
+
|
|
14
|
+
[xSmallMedia, smallMedia, mediumMedia, largeMedia].forEach(media => {
|
|
15
|
+
media.addListener(() => {
|
|
16
|
+
handlers.forEach(handler => handler());
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const sizes = () => {
|
|
21
|
+
return {
|
|
22
|
+
"screen-x-small": xSmallMedia.matches,
|
|
23
|
+
"screen-small": smallMedia.matches,
|
|
24
|
+
"screen-medium": mediumMedia.matches,
|
|
25
|
+
"screen-large": largeMedia.matches
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const subscribe = handler => handlers.push(handler);
|
|
30
|
+
|
|
31
|
+
export const unsubscribe = handler => {
|
|
32
|
+
handlers = handlers.filter(item => item !== handler);
|
|
33
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<form @submit.prevent="onSubmit">
|
|
3
|
+
<dx-form :form-data="formData" :disabled="loading">
|
|
4
|
+
<dx-item
|
|
5
|
+
data-field="password"
|
|
6
|
+
editor-type="dxTextBox"
|
|
7
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Password', mode: 'password' }"
|
|
8
|
+
>
|
|
9
|
+
<dx-required-rule message="Password is required" />
|
|
10
|
+
<dx-label :visible="false" />
|
|
11
|
+
</dx-item>
|
|
12
|
+
<dx-item
|
|
13
|
+
data-field="confirmedPassword"
|
|
14
|
+
editor-type="dxTextBox"
|
|
15
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' }"
|
|
16
|
+
>
|
|
17
|
+
<dx-required-rule message="Password is required" />
|
|
18
|
+
<dx-custom-rule
|
|
19
|
+
message="Passwords do not match"
|
|
20
|
+
:validation-callback="confirmPassword"
|
|
21
|
+
/>
|
|
22
|
+
<dx-label :visible="false" />
|
|
23
|
+
</dx-item>
|
|
24
|
+
<dx-button-item>
|
|
25
|
+
<dx-button-options
|
|
26
|
+
width="100%"
|
|
27
|
+
type="default"
|
|
28
|
+
template="changePassword"
|
|
29
|
+
:use-submit-behavior="true"
|
|
30
|
+
>
|
|
31
|
+
</dx-button-options>
|
|
32
|
+
</dx-button-item>
|
|
33
|
+
|
|
34
|
+
<template #changePassword>
|
|
35
|
+
<div>
|
|
36
|
+
<span class="dx-button-text">
|
|
37
|
+
<dx-loadIndicator v-if="loading" width="24px" height="24px" :visible="true" />
|
|
38
|
+
<span v-if="!loading">Continue</span>
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
</dx-form>
|
|
43
|
+
</form>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script>
|
|
47
|
+
import DxForm, {
|
|
48
|
+
DxItem,
|
|
49
|
+
DxLabel,
|
|
50
|
+
DxButtonItem,
|
|
51
|
+
DxButtonOptions,
|
|
52
|
+
DxCustomRule,
|
|
53
|
+
DxRequiredRule
|
|
54
|
+
} from 'devextreme-vue/form';
|
|
55
|
+
import DxLoadIndicator from 'devextreme-vue/load-indicator';
|
|
56
|
+
import notify from 'devextreme/ui/notify';
|
|
57
|
+
import { useRouter, useRoute } from 'vue-router';
|
|
58
|
+
import { ref, reactive } from "vue";
|
|
59
|
+
|
|
60
|
+
import auth from "../auth";
|
|
61
|
+
|
|
62
|
+
export default {
|
|
63
|
+
components: {
|
|
64
|
+
DxForm,
|
|
65
|
+
DxItem,
|
|
66
|
+
DxLabel,
|
|
67
|
+
DxButtonItem,
|
|
68
|
+
DxButtonOptions,
|
|
69
|
+
DxRequiredRule,
|
|
70
|
+
DxCustomRule,
|
|
71
|
+
DxLoadIndicator
|
|
72
|
+
},
|
|
73
|
+
setup() {
|
|
74
|
+
const router = useRouter();
|
|
75
|
+
const route = useRoute();
|
|
76
|
+
|
|
77
|
+
const recoveryCode = ref("");
|
|
78
|
+
const loading = ref(false);
|
|
79
|
+
const formData = reactive({
|
|
80
|
+
password:""
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
recoveryCode.value = route.params.recoveryCode;
|
|
84
|
+
|
|
85
|
+
async function onSubmit() {
|
|
86
|
+
const { password } = formData;
|
|
87
|
+
loading.value = true;
|
|
88
|
+
|
|
89
|
+
const result = await auth.changePassword(password, recoveryCode.value);
|
|
90
|
+
loading.value = false;
|
|
91
|
+
|
|
92
|
+
if (result.isOk) {
|
|
93
|
+
router.push("/login-form");
|
|
94
|
+
} else {
|
|
95
|
+
notify(result.message, 'error', 2000);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function confirmPassword (e) {
|
|
100
|
+
return e.value === formData.password;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
loading,
|
|
105
|
+
formData,
|
|
106
|
+
onSubmit,
|
|
107
|
+
confirmPassword
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<style>
|
|
114
|
+
|
|
115
|
+
</style>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<form class="create-account-form" @submit.prevent="onSubmit">
|
|
3
|
+
<dx-form :form-data="formData" :disabled="loading">
|
|
4
|
+
<dx-item
|
|
5
|
+
data-field="email"
|
|
6
|
+
editor-type="dxTextBox"
|
|
7
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Email', mode: 'email' }"
|
|
8
|
+
>
|
|
9
|
+
<dx-required-rule message="Email is required" />
|
|
10
|
+
<dx-email-rule message="Email is invalid" />
|
|
11
|
+
<dx-label :visible="false" />
|
|
12
|
+
</dx-item>
|
|
13
|
+
<dx-item
|
|
14
|
+
data-field="password"
|
|
15
|
+
editor-type="dxTextBox"
|
|
16
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Password', mode: 'password' }"
|
|
17
|
+
>
|
|
18
|
+
<dx-required-rule message="Password is required" />
|
|
19
|
+
<dx-label :visible="false" />
|
|
20
|
+
</dx-item>
|
|
21
|
+
<dx-item
|
|
22
|
+
data-field="confirmedPassword"
|
|
23
|
+
editor-type="dxTextBox"
|
|
24
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' }"
|
|
25
|
+
>
|
|
26
|
+
<dx-required-rule message="Password is required" />
|
|
27
|
+
<dx-custom-rule
|
|
28
|
+
message="Passwords do not match"
|
|
29
|
+
:validation-callback="confirmPassword"
|
|
30
|
+
/>
|
|
31
|
+
<dx-label :visible="false" />
|
|
32
|
+
</dx-item>
|
|
33
|
+
<dx-item>
|
|
34
|
+
<template #default>
|
|
35
|
+
<div class='policy-info'>
|
|
36
|
+
By creating an account, you agree to the <router-link to="#">Terms of Service</router-link> and <router-link to="#">Privacy Policy</router-link>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
</dx-item>
|
|
40
|
+
<dx-button-item>
|
|
41
|
+
<dx-button-options
|
|
42
|
+
width="100%"
|
|
43
|
+
type="default"
|
|
44
|
+
template="createAccount"
|
|
45
|
+
:use-submit-behavior="true"
|
|
46
|
+
>
|
|
47
|
+
</dx-button-options>
|
|
48
|
+
</dx-button-item>
|
|
49
|
+
<dx-item>
|
|
50
|
+
<template #default>
|
|
51
|
+
<div class="login-link">
|
|
52
|
+
Have an account? <router-link to="/login-form">Sign In</router-link>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
</dx-item>
|
|
56
|
+
<template #createAccount>
|
|
57
|
+
<div>
|
|
58
|
+
<span class="dx-button-text">
|
|
59
|
+
<dx-loadIndicator v-if="loading" width="24px" height="24px" :visible="true" />
|
|
60
|
+
<span v-if="!loading">Create a new account</span>
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
</dx-form>
|
|
65
|
+
</form>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script>
|
|
69
|
+
import DxForm, {
|
|
70
|
+
DxItem,
|
|
71
|
+
DxLabel,
|
|
72
|
+
DxButtonItem,
|
|
73
|
+
DxButtonOptions,
|
|
74
|
+
DxCustomRule,
|
|
75
|
+
DxRequiredRule,
|
|
76
|
+
DxEmailRule
|
|
77
|
+
} from 'devextreme-vue/form';
|
|
78
|
+
import DxLoadIndicator from 'devextreme-vue/load-indicator';
|
|
79
|
+
import notify from 'devextreme/ui/notify';
|
|
80
|
+
import { useRouter } from 'vue-router';
|
|
81
|
+
import { ref, reactive } from 'vue';
|
|
82
|
+
|
|
83
|
+
import auth from "../auth";
|
|
84
|
+
|
|
85
|
+
export default {
|
|
86
|
+
components: {
|
|
87
|
+
DxForm,
|
|
88
|
+
DxItem,
|
|
89
|
+
DxLabel,
|
|
90
|
+
DxButtonItem,
|
|
91
|
+
DxButtonOptions,
|
|
92
|
+
DxRequiredRule,
|
|
93
|
+
DxCustomRule,
|
|
94
|
+
DxEmailRule,
|
|
95
|
+
DxLoadIndicator
|
|
96
|
+
},
|
|
97
|
+
setup() {
|
|
98
|
+
const router = useRouter();
|
|
99
|
+
|
|
100
|
+
const loading = ref(false);
|
|
101
|
+
const formData = reactive({
|
|
102
|
+
email:"",
|
|
103
|
+
password:""
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const onSubmit = async () => {
|
|
107
|
+
const { email, password } = formData;
|
|
108
|
+
loading.value = true;
|
|
109
|
+
|
|
110
|
+
const result = await auth.createAccount(email, password);
|
|
111
|
+
loading.value = false;
|
|
112
|
+
|
|
113
|
+
if (result.isOk) {
|
|
114
|
+
router.push("/login-form");
|
|
115
|
+
} else {
|
|
116
|
+
notify(result.message, 'error', 2000);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
function confirmPassword(e) {
|
|
121
|
+
return e.value === formData.password;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
formData,
|
|
126
|
+
loading,
|
|
127
|
+
onSubmit,
|
|
128
|
+
confirmPassword
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<style lang="scss">
|
|
135
|
+
@import "../themes/generated/variables.base.scss";
|
|
136
|
+
|
|
137
|
+
.create-account-form {
|
|
138
|
+
.policy-info {
|
|
139
|
+
margin: 10px 0;
|
|
140
|
+
color: rgba($base-text-color, alpha($base-text-color) * 0.7);
|
|
141
|
+
font-size: 14px;
|
|
142
|
+
font-style: normal;
|
|
143
|
+
|
|
144
|
+
a {
|
|
145
|
+
color: rgba($base-text-color, alpha($base-text-color) * 0.7);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.login-link {
|
|
150
|
+
color: $base-accent;
|
|
151
|
+
font-size: 16px;
|
|
152
|
+
text-align: center;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<form class="login-form" @submit.prevent="onSubmit">
|
|
3
|
+
<dx-form :form-data="formData" :disabled="loading">
|
|
4
|
+
<dx-item
|
|
5
|
+
data-field="email"
|
|
6
|
+
editor-type="dxTextBox"
|
|
7
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Email', mode: 'email' }"
|
|
8
|
+
>
|
|
9
|
+
<dx-required-rule message="Email is required" />
|
|
10
|
+
<dx-email-rule message="Email is invalid" />
|
|
11
|
+
<dx-label :visible="false" />
|
|
12
|
+
</dx-item>
|
|
13
|
+
<dx-item
|
|
14
|
+
data-field='password'
|
|
15
|
+
editor-type='dxTextBox'
|
|
16
|
+
:editor-options="{ stylingMode: 'filled', placeholder: 'Password', mode: 'password' }"
|
|
17
|
+
>
|
|
18
|
+
<dx-required-rule message="Password is required" />
|
|
19
|
+
<dx-label :visible="false" />
|
|
20
|
+
</dx-item>
|
|
21
|
+
<dx-item
|
|
22
|
+
data-field="rememberMe"
|
|
23
|
+
editor-type="dxCheckBox"
|
|
24
|
+
:editor-options="{ text: 'Remember me', elementAttr: { class: 'form-text' } }"
|
|
25
|
+
>
|
|
26
|
+
<dx-label :visible="false" />
|
|
27
|
+
</dx-item>
|
|
28
|
+
<dx-button-item>
|
|
29
|
+
<dx-button-options
|
|
30
|
+
width="100%"
|
|
31
|
+
type="default"
|
|
32
|
+
template="signInTemplate"
|
|
33
|
+
:use-submit-behavior="true"
|
|
34
|
+
>
|
|
35
|
+
</dx-button-options>
|
|
36
|
+
</dx-button-item>
|
|
37
|
+
<dx-item>
|
|
38
|
+
<template #default>
|
|
39
|
+
<div class="link">
|
|
40
|
+
<router-link to="/reset-password">Forgot password?</router-link>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
</dx-item>
|
|
44
|
+
<dx-button-item>
|
|
45
|
+
<dx-button-options
|
|
46
|
+
text="Create an account"
|
|
47
|
+
width="100%"
|
|
48
|
+
:on-click="onCreateAccountClick"
|
|
49
|
+
/>
|
|
50
|
+
</dx-button-item>
|
|
51
|
+
<template #signInTemplate>
|
|
52
|
+
<div>
|
|
53
|
+
<span class="dx-button-text">
|
|
54
|
+
<dx-load-indicator v-if="loading" width="24px" height="24px" :visible="true" />
|
|
55
|
+
<span v-if="!loading">Sign In</span>
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
</dx-form>
|
|
60
|
+
</form>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script>
|
|
64
|
+
import DxLoadIndicator from "devextreme-vue/load-indicator";
|
|
65
|
+
import DxForm, {
|
|
66
|
+
DxItem,
|
|
67
|
+
DxEmailRule,
|
|
68
|
+
DxRequiredRule,
|
|
69
|
+
DxLabel,
|
|
70
|
+
DxButtonItem,
|
|
71
|
+
DxButtonOptions
|
|
72
|
+
} from "devextreme-vue/form";
|
|
73
|
+
import notify from 'devextreme/ui/notify';
|
|
74
|
+
|
|
75
|
+
import auth from "../auth";
|
|
76
|
+
|
|
77
|
+
import { reactive, ref } from 'vue';
|
|
78
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
setup() {
|
|
82
|
+
const route = useRoute();
|
|
83
|
+
const router = useRouter();
|
|
84
|
+
|
|
85
|
+
const formData = reactive({
|
|
86
|
+
email:"",
|
|
87
|
+
password:""
|
|
88
|
+
});
|
|
89
|
+
const loading = ref(false);
|
|
90
|
+
|
|
91
|
+
function onCreateAccountClick() {
|
|
92
|
+
router.push("/create-account");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function onSubmit() {
|
|
96
|
+
const { email, password } = formData;
|
|
97
|
+
loading.value = true;
|
|
98
|
+
const result = await auth.logIn(email, password);
|
|
99
|
+
if (!result.isOk) {
|
|
100
|
+
loading.value = false;
|
|
101
|
+
notify(result.message, "error", 2000);
|
|
102
|
+
} else {
|
|
103
|
+
router.push(route.query.redirect || "/home");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
formData,
|
|
109
|
+
loading,
|
|
110
|
+
onCreateAccountClick,
|
|
111
|
+
onSubmit
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
components: {
|
|
115
|
+
DxLoadIndicator,
|
|
116
|
+
DxForm,
|
|
117
|
+
DxEmailRule,
|
|
118
|
+
DxRequiredRule,
|
|
119
|
+
DxItem,
|
|
120
|
+
DxLabel,
|
|
121
|
+
DxButtonItem,
|
|
122
|
+
DxButtonOptions
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<style lang="scss">
|
|
128
|
+
@import "../themes/generated/variables.base.scss";
|
|
129
|
+
|
|
130
|
+
.login-form {
|
|
131
|
+
.link {
|
|
132
|
+
text-align: center;
|
|
133
|
+
font-size: 16px;
|
|
134
|
+
font-style: normal;
|
|
135
|
+
|
|
136
|
+
a {
|
|
137
|
+
text-decoration: none;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.form-text {
|
|
142
|
+
margin: 10px 0;
|
|
143
|
+
color: rgba($base-text-color, alpha($base-text-color) * 0.7);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
</style>
|