devextreme-cli 1.11.0-alpha.0 → 1.11.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.
Files changed (100) hide show
  1. package/package.json +10 -9
  2. package/src/application.js +40 -20
  3. package/src/applications/application.angular.js +11 -3
  4. package/src/applications/application.nextjs.js +231 -0
  5. package/src/applications/application.react.js +16 -6
  6. package/src/applications/application.vue.js +6 -11
  7. package/src/templates/nextjs/application/.env +1 -0
  8. package/src/templates/nextjs/application/devextreme.json +63 -0
  9. package/src/templates/nextjs/application/next.config.mjs +32 -0
  10. package/src/templates/nextjs/application/public/logo192.png +0 -0
  11. package/src/templates/nextjs/application/public/logo512.png +0 -0
  12. package/src/templates/nextjs/application/public/manifest.json +25 -0
  13. package/src/templates/nextjs/application/public/robots.txt +3 -0
  14. package/src/templates/nextjs/application/src/app/actions/auth.ts +76 -0
  15. package/src/templates/nextjs/application/src/app/auth/[type]/page.tsx +49 -0
  16. package/src/templates/nextjs/application/src/app/layout.tsx +17 -0
  17. package/src/templates/nextjs/application/src/app/lib/session.ts +47 -0
  18. package/src/templates/nextjs/application/src/app/pages/layout.tsx +18 -0
  19. package/src/templates/nextjs/application/src/app-info.tsx +5 -0
  20. package/src/templates/nextjs/application/src/app-navigation.tsx +21 -0
  21. package/src/templates/nextjs/application/src/components/change-password-form/ChangePasswordForm.tsx +86 -0
  22. package/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.scss +19 -0
  23. package/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.tsx +107 -0
  24. package/src/templates/nextjs/application/src/components/footer/Footer.scss +12 -0
  25. package/src/templates/nextjs/application/src/components/footer/Footer.tsx +5 -0
  26. package/src/templates/nextjs/application/src/components/header/Header.scss +40 -0
  27. package/src/templates/nextjs/application/src/components/header/Header.tsx +38 -0
  28. package/src/templates/nextjs/application/src/components/index.tsx +7 -0
  29. package/src/templates/nextjs/application/src/components/login-form/LoginForm.scss +12 -0
  30. package/src/templates/nextjs/application/src/components/login-form/LoginForm.tsx +101 -0
  31. package/src/templates/nextjs/application/src/components/reset-password-form/ResetPasswordForm.scss +12 -0
  32. package/src/templates/nextjs/application/src/components/reset-password-form/ResetPasswordForm.tsx +78 -0
  33. package/src/templates/nextjs/application/src/components/side-navigation-menu/SideNavigationMenu.scss +71 -0
  34. package/src/templates/nextjs/application/src/components/side-navigation-menu/SideNavigationMenu.tsx +88 -0
  35. package/src/templates/nextjs/application/src/components/theme-switcher/ThemeSwitcher.tsx +21 -0
  36. package/src/templates/nextjs/application/src/components/user-panel/UserPanel.scss +51 -0
  37. package/src/templates/nextjs/application/src/components/user-panel/UserPanel.tsx +55 -0
  38. package/src/templates/nextjs/application/src/dx-styles.scss +106 -0
  39. package/src/templates/nextjs/application/src/index.css +12 -0
  40. package/src/templates/nextjs/application/src/layouts/index.tsx +3 -0
  41. package/src/templates/nextjs/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.scss +17 -0
  42. package/src/templates/nextjs/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.tsx +133 -0
  43. package/src/templates/nextjs/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.scss +10 -0
  44. package/src/templates/nextjs/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.tsx +119 -0
  45. package/src/templates/nextjs/application/src/layouts/single-card/single-card.scss +42 -0
  46. package/src/templates/nextjs/application/src/layouts/single-card/single-card.tsx +16 -0
  47. package/src/templates/nextjs/application/src/middleware.ts +46 -0
  48. package/src/templates/nextjs/application/src/theme.tsx +66 -0
  49. package/src/templates/nextjs/application/src/themes/metadata.additional.dark.json +11 -0
  50. package/src/templates/nextjs/application/src/themes/metadata.additional.json +11 -0
  51. package/src/templates/nextjs/application/src/themes/metadata.base.dark.json +8 -0
  52. package/src/templates/nextjs/application/src/themes/metadata.base.json +7 -0
  53. package/src/templates/nextjs/application/src/types.tsx +60 -0
  54. package/src/templates/nextjs/application/src/utils/default-user.tsx +7 -0
  55. package/src/templates/nextjs/application/src/utils/media-query.tsx +56 -0
  56. package/src/templates/nextjs/application/src/variables.scss +53 -0
  57. package/src/templates/nextjs/page/page.scss +0 -0
  58. package/src/templates/nextjs/page/page.tsx +13 -0
  59. package/src/templates/nextjs/sample-pages/home/home.scss +37 -0
  60. package/src/templates/nextjs/sample-pages/home/page.tsx +101 -0
  61. package/src/templates/nextjs/sample-pages/profile/page.tsx +61 -0
  62. package/src/templates/nextjs/sample-pages/profile/profile.scss +19 -0
  63. package/src/templates/nextjs/sample-pages/tasks/page.tsx +112 -0
  64. package/src/templates/nextjs/sample-pages/tasks/tasks.scss +3 -0
  65. package/src/templates/react/application/src/App.tsx +2 -1
  66. package/src/templates/react/application/src/Content.tsx +1 -1
  67. package/src/templates/react/application/src/app-routes.tsx +3 -3
  68. package/src/templates/react/application/src/components/change-password-form/ChangePasswordForm.tsx +1 -1
  69. package/src/templates/react/application/src/components/create-account-form/CreateAccountForm.tsx +1 -1
  70. package/src/templates/react/application/src/components/header/Header.scss +1 -1
  71. package/src/templates/react/application/src/components/login-form/LoginForm.tsx +1 -1
  72. package/src/templates/react/application/src/components/side-navigation-menu/SideNavigationMenu.tsx +2 -2
  73. package/src/templates/react/application/src/components/user-panel/UserPanel.tsx +1 -1
  74. package/src/templates/react/application/src/contexts/auth-hooks.ts +8 -0
  75. package/src/templates/react/application/src/contexts/auth.tsx +7 -5
  76. package/src/templates/react/application/src/contexts/navigation-hooks.ts +22 -0
  77. package/src/templates/react/application/src/contexts/navigation.tsx +3 -17
  78. package/src/templates/react/application/src/dx-styles.scss +3 -3
  79. package/src/templates/react/application/src/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.tsx +3 -3
  80. package/src/templates/react/application/src/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.tsx +3 -3
  81. package/src/templates/react/application/src/types.tsx +2 -2
  82. package/src/templates/react/page/page.tsx +1 -1
  83. package/src/templates/react/sample-pages/home/home.tsx +1 -1
  84. package/src/templates/react/sample-pages/index.tsx +3 -3
  85. package/src/templates/react/sample-pages/profile/profile.tsx +1 -1
  86. package/src/templates/react/sample-pages/tasks/tasks.tsx +1 -1
  87. package/src/templates/vue-v3/application/eslint.config.js +32 -0
  88. package/src/templates/vue-v3/application/src/App.vue +1 -1
  89. package/src/templates/vue-v3/application/src/components/header-toolbar.vue +2 -2
  90. package/src/templates/vue-v3/application/src/dx-styles.scss +4 -3
  91. package/src/templates/vue-v3/application/src/layouts/side-nav-inner-toolbar.vue +2 -2
  92. package/src/templates/vue-v3/application/src/layouts/side-nav-outer-toolbar.vue +2 -2
  93. package/src/templates/vue-v3/application/src/main.js +1 -1
  94. package/src/templates/vue-v3/application/src/router.js +5 -5
  95. package/src/utility/latest-versions.js +7 -4
  96. package/src/utility/module.js +13 -5
  97. package/src/utility/prompts/react-app-type.js +17 -0
  98. package/src/utility/run-command.js +10 -2
  99. package/src/utility/template-creator.js +8 -4
  100. package/src/templates/vue-v3/application/vue.config.js +0 -1
@@ -0,0 +1,25 @@
1
+ {
2
+ "short_name": "React App",
3
+ "name": "Create React App Sample",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ },
10
+ {
11
+ "src": "logo192.png",
12
+ "type": "image/png",
13
+ "sizes": "192x192"
14
+ },
15
+ {
16
+ "src": "logo512.png",
17
+ "type": "image/png",
18
+ "sizes": "512x512"
19
+ }
20
+ ],
21
+ "start_url": ".",
22
+ "display": "standalone",
23
+ "theme_color": "#000000",
24
+ "background_color": "#ffffff"
25
+ }
@@ -0,0 +1,3 @@
1
+ # https://www.robotstxt.org/robotstxt.html
2
+ User-agent: *
3
+ Disallow:
@@ -0,0 +1,76 @@
1
+ 'use server'
2
+ import { redirect } from 'next/navigation'
3
+ import defaultUser from '@/utils/default-user';
4
+ import { createSession, deleteSession } from '@/app/lib/session'
5
+
6
+ export async function signUp(email<%=#isTypeScript%>: string<%=/isTypeScript%>, password<%=#isTypeScript%>: string<%=/isTypeScript%>) {
7
+ try {
8
+ // 1. Check if the user exists in the database and return isOk: false if so;
9
+ // 2. Otherwise, add the user to the database.
10
+ console.log(email, password);
11
+
12
+ return {
13
+ isOk: true,
14
+ }
15
+ } catch {
16
+ return {
17
+ isOk: false,
18
+ message: 'Unable to create an account',
19
+ }
20
+ }
21
+ }
22
+
23
+ export async function signIn(email<%=#isTypeScript%>: string<%=/isTypeScript%>, password<%=#isTypeScript%>: string<%=/isTypeScript%>) {
24
+ try {
25
+ // Verify that a user exists
26
+ console.log(email, password);
27
+
28
+ await createSession(defaultUser.id);
29
+
30
+ return {
31
+ isOk: true,
32
+ }
33
+ } catch {
34
+ return {
35
+ isOk: false,
36
+ message: 'Unable to sign in',
37
+ }
38
+ }
39
+ }
40
+
41
+ export async function signOut() {
42
+ await deleteSession();
43
+ redirect('/login');
44
+ }
45
+
46
+ export async function changePassword(email<%=#isTypeScript%>: string<%=/isTypeScript%>, recoveryCode<%=#isTypeScript%>?: string<%=/isTypeScript%>) {
47
+ try {
48
+ // Verify the recovery code
49
+ console.log(email, recoveryCode);
50
+
51
+ return {
52
+ isOk: true,
53
+ }
54
+ } catch {
55
+ return {
56
+ isOk: false,
57
+ message: 'Unable to change the password',
58
+ }
59
+ }
60
+ }
61
+
62
+ export async function resetPassword(email<%=#isTypeScript%>: string<%=/isTypeScript%>) {
63
+ try {
64
+ // Reset password
65
+ console.log(email);
66
+
67
+ return {
68
+ isOk: true,
69
+ }
70
+ } catch {
71
+ return {
72
+ isOk: false,
73
+ message: 'Unable to reset password',
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,49 @@
1
+ 'use client'
2
+ import { use } from 'react';
3
+ import { notFound } from 'next/navigation';
4
+ import { SingleCard } from '@/layouts';
5
+ import {
6
+ LoginForm,
7
+ CreateAccountForm,
8
+ ResetPasswordForm,
9
+ ChangePasswordForm,
10
+ } from '@/components';
11
+
12
+ const formText<%=#isTypeScript%>: Record<string, Record<string, string>><%=/isTypeScript%> = {
13
+ 'login': {
14
+ title: 'Sign In'
15
+ },
16
+ 'create-account': {
17
+ title: 'Sign Up'
18
+ },
19
+ 'reset-password': {
20
+ title: 'Reset Password',
21
+ description: 'Please enter the email address that you used to register, and we will send you a link to reset your password via Email.'
22
+ },
23
+ 'change-password': {
24
+ title: 'Change Password',
25
+ }
26
+ }
27
+
28
+ function AuthForm({name}<%=#isTypeScript%>: {name: string}<%=/isTypeScript%>) {
29
+ switch (name) {
30
+ case 'login': return <LoginForm />;
31
+ case 'create-account': return <CreateAccountForm />;
32
+ case 'reset-password': return <ResetPasswordForm />;
33
+ case 'change-password': return <ChangePasswordForm />;
34
+ }
35
+ }
36
+
37
+ export default function AuthPage({ params }<%=#isTypeScript%>: {params: Promise<{type: string}>}<%=/isTypeScript%>) {
38
+ const { type } = use(params)
39
+
40
+ if (!formText[type]) {
41
+ notFound();
42
+ }
43
+
44
+ const { title, description } = formText[type];
45
+
46
+ return <SingleCard title={title} description={description}>
47
+ <AuthForm name={type}/>
48
+ </SingleCard>
49
+ }
@@ -0,0 +1,17 @@
1
+ <%=#isTypeScript%>import type { PropsWithChildren } from 'react';
2
+ <%=/isTypeScript%>import { ThemeProvider } from "@/theme";
3
+
4
+ export default function RootLayout({ children }<%=#isTypeScript%>: PropsWithChildren<object><%=/isTypeScript%>) {
5
+ return (
6
+ <html lang="en">
7
+ <title>NextJs Dx App</title>
8
+ <body className="dx-viewport">
9
+ <ThemeProvider>
10
+ <div className='app'>
11
+ {children}
12
+ </div>
13
+ </ThemeProvider>
14
+ </body>
15
+ </html>
16
+ );
17
+ }
@@ -0,0 +1,47 @@
1
+ import 'server-only';
2
+ import { SignJWT, jwtVerify } from 'jose';
3
+ import { cookies } from 'next/headers';
4
+ <%=#isTypeScript%>import type { SessionPayload } from '@/types';
5
+ <%=/isTypeScript%>
6
+ const secretKey = process.env.SESSION_SECRET;
7
+ const encoder = new TextEncoder();
8
+ const encodedKey = encoder.encode(secretKey);
9
+
10
+ export async function encrypt(payload<%=#isTypeScript%>: SessionPayload<%=/isTypeScript%>) {
11
+ return new SignJWT(payload)
12
+ .setProtectedHeader({ alg: 'HS256' })
13
+ .setIssuedAt()
14
+ .setExpirationTime('7d')
15
+ .sign(encodedKey);
16
+ }
17
+
18
+ export async function decrypt(session<%=#isTypeScript%>: string | undefined = ''<%=/isTypeScript%>) {
19
+ try {
20
+ const { payload } = await jwtVerify(session, encodedKey, {
21
+ algorithms: ['HS256'],
22
+ });
23
+
24
+ return payload;
25
+ } catch {
26
+ console.log('Failed to verify session');
27
+ }
28
+ }
29
+
30
+ export async function createSession(userId<%=#isTypeScript%>: string<%=/isTypeScript%>) {
31
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
32
+ const session = await encrypt({ userId, expiresAt });
33
+ const cookieStore = await cookies();
34
+
35
+ cookieStore.set('session', session, {
36
+ httpOnly: true,
37
+ secure: true,
38
+ expires: expiresAt,
39
+ sameSite: 'lax',
40
+ path: '/',
41
+ });
42
+ }
43
+
44
+ export async function deleteSession() {
45
+ const cookieStore = await cookies();
46
+ cookieStore.delete('session');
47
+ }
@@ -0,0 +1,18 @@
1
+ <%=#isTypeScript%>import type { PropsWithChildren } from 'react';
2
+ <%=/isTypeScript%>import appInfo from '@/app-info';
3
+ import { Footer } from '@/components';
4
+ import { <%=layout%> as SideNavBarLayout } from '@/layouts';
5
+
6
+ export default function Content({children}<%=#isTypeScript%>: PropsWithChildren<object><%=/isTypeScript%>) {
7
+ return (
8
+ <SideNavBarLayout title={appInfo.title}>
9
+ {children}
10
+ <Footer>
11
+ Copyright © 2011-{new Date().getFullYear()} {appInfo.title} Inc.
12
+ <br />
13
+ All trademarks or registered trademarks are property of their
14
+ respective owners.
15
+ </Footer>
16
+ </SideNavBarLayout>
17
+ );
18
+ }
@@ -0,0 +1,5 @@
1
+ const appInfo = {
2
+ title: '<%=project%>'
3
+ };
4
+ export default appInfo;
5
+
@@ -0,0 +1,21 @@
1
+ export const navigation = [<%=^empty%>
2
+ {
3
+ text: 'Home',
4
+ path: '/pages/home',
5
+ icon: 'home'
6
+ },
7
+ {
8
+ text: 'Examples',
9
+ icon: 'folder',
10
+ items: [
11
+ {
12
+ text: 'Profile',
13
+ path: '/pages/profile'
14
+ },
15
+ {
16
+ text: 'Tasks',
17
+ path: '/pages/tasks'
18
+ }
19
+ ]
20
+ }
21
+ <%=/empty%>];
@@ -0,0 +1,86 @@
1
+ 'use client'
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+ import Form, {
5
+ Item,
6
+ Label,
7
+ ButtonItem,
8
+ ButtonOptions,
9
+ RequiredRule,
10
+ CustomRule,
11
+ } from 'devextreme-react/form';
12
+ import LoadIndicator from 'devextreme-react/load-indicator';
13
+ import notify from 'devextreme/ui/notify';
14
+ <%=#isTypeScript%>import { ValidationCallbackData } from 'devextreme-react/common';<%=/isTypeScript%>
15
+ import { changePassword } from '@/app/actions/auth';
16
+
17
+ export default function ChangePasswordForm() {
18
+ const router = useRouter();
19
+ const [loading, setLoading] = useState(false);
20
+ const formData = useRef({ password: '' });
21
+
22
+ const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
23
+ e.preventDefault();
24
+ const { password } = formData.current;
25
+ setLoading(true);
26
+
27
+ const result = await changePassword(password);
28
+ setLoading(false);
29
+
30
+ if (result.isOk) {
31
+ router.push('/login');
32
+ } else {
33
+ notify(result.message, 'error', 2000);
34
+ }
35
+ }, [router]);
36
+
37
+ const confirmPassword = useCallback(
38
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
39
+ []
40
+ );
41
+
42
+ return (
43
+ <form onSubmit={onSubmit}>
44
+ <Form formData={formData.current} disabled={loading}>
45
+ <Item
46
+ dataField={'password'}
47
+ editorType={'dxTextBox'}
48
+ editorOptions={passwordEditorOptions}
49
+ >
50
+ <RequiredRule message="Password is required" />
51
+ <Label visible={false} />
52
+ </Item>
53
+ <Item
54
+ dataField={'confirmedPassword'}
55
+ editorType={'dxTextBox'}
56
+ editorOptions={confirmedPasswordEditorOptions}
57
+ >
58
+ <RequiredRule message="Password is required" />
59
+ <CustomRule
60
+ message={'Passwords do not match'}
61
+ validationCallback={confirmPassword}
62
+ />
63
+ <Label visible={false} />
64
+ </Item>
65
+ <ButtonItem>
66
+ <ButtonOptions
67
+ width={'100%'}
68
+ type={'default'}
69
+ useSubmitBehavior={true}
70
+ >
71
+ <span className="dx-button-text">
72
+ {
73
+ loading
74
+ ? <LoadIndicator width={'24px'} height={'24px'} visible={true} />
75
+ : 'Continue'
76
+ }
77
+ </span>
78
+ </ButtonOptions>
79
+ </ButtonItem>
80
+ </Form>
81
+ </form>
82
+ );
83
+ }
84
+
85
+ const passwordEditorOptions = { stylingMode: 'filled', placeholder: 'Password', mode: 'password' };
86
+ const confirmedPasswordEditorOptions = { stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' };
@@ -0,0 +1,19 @@
1
+ .create-account-form {
2
+ .policy-info {
3
+ color: var(--base-text-color-alpha-7);
4
+ font-size: 12px;
5
+ font-style: normal;
6
+
7
+ a {
8
+ color: var(--base-text-color-alpha-7);
9
+ }
10
+ }
11
+
12
+ .login-link {
13
+ color: var(--base-accent);
14
+ font-size: 12px;
15
+ text-align: center;
16
+ padding: 6px 0 32px 0;
17
+ border-bottom: 1px solid var(--border-color);
18
+ }
19
+ }
@@ -0,0 +1,107 @@
1
+ 'use client'
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+ import Link from 'next/link';
5
+ import Form, {
6
+ Item,
7
+ Label,
8
+ ButtonItem,
9
+ ButtonOptions,
10
+ RequiredRule,
11
+ CustomRule,
12
+ EmailRule
13
+ } from 'devextreme-react/form';
14
+ import notify from 'devextreme/ui/notify';
15
+ import LoadIndicator from 'devextreme-react/load-indicator';
16
+ import { signUp } from '@/app/actions/auth';
17
+ <%=#isTypeScript%>import { ValidationCallbackData } from 'devextreme-react/common';<%=/isTypeScript%>
18
+ import './CreateAccountForm.scss';
19
+
20
+ export default function CreateAccountForm() {
21
+ const router = useRouter();
22
+ const [loading, setLoading] = useState(false);
23
+ const formData = useRef({ email: '', password: '' });
24
+
25
+ const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
26
+ e.preventDefault();
27
+ const { email, password } = formData.current;
28
+ setLoading(true);
29
+
30
+ const result = await signUp(email, password);
31
+ setLoading(false);
32
+
33
+ if (result.isOk) {
34
+ router.push('/login');
35
+ } else {
36
+ notify(result.message, 'error', 2000);
37
+ }
38
+ }, [router]);
39
+
40
+ const confirmPassword = useCallback(
41
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
42
+ []
43
+ );
44
+
45
+ return (
46
+ <form className={'create-account-form'} onSubmit={onSubmit}>
47
+ <Form formData={formData.current} disabled={loading}>
48
+ <Item
49
+ dataField={'email'}
50
+ editorType={'dxTextBox'}
51
+ editorOptions={emailEditorOptions}
52
+ >
53
+ <RequiredRule message="Email is required" />
54
+ <EmailRule message="Email is invalid" />
55
+ <Label visible={false} />
56
+ </Item>
57
+ <Item
58
+ dataField={'password'}
59
+ editorType={'dxTextBox'}
60
+ editorOptions={passwordEditorOptions}
61
+ >
62
+ <RequiredRule message="Password is required" />
63
+ <Label visible={false} />
64
+ </Item>
65
+ <Item
66
+ dataField={'confirmedPassword'}
67
+ editorType={'dxTextBox'}
68
+ editorOptions={confirmedPasswordEditorOptions}
69
+ >
70
+ <RequiredRule message="Password is required" />
71
+ <CustomRule
72
+ message={'Passwords do not match'}
73
+ validationCallback={confirmPassword}
74
+ />
75
+ <Label visible={false} />
76
+ </Item>
77
+ <Item>
78
+ <div className='policy-info'>
79
+ By creating an account, you agree to the <Link href="#">Terms of Service</Link> and <Link href="#">Privacy Policy</Link>
80
+ </div>
81
+ </Item>
82
+ <ButtonItem>
83
+ <ButtonOptions
84
+ width={'100%'}
85
+ type={'default'}
86
+ useSubmitBehavior={true}
87
+ >
88
+ <span className="dx-button-text">
89
+ {
90
+ loading
91
+ ? <LoadIndicator width={'24px'} height={'24px'} visible={true} />
92
+ : 'Create a new account'
93
+ }
94
+ </span>
95
+ </ButtonOptions>
96
+ </ButtonItem>
97
+ </Form>
98
+ <div className={'login-link'}>
99
+ Have an account? <Link href={'/login'}>Sign In</Link>
100
+ </div>
101
+ </form>
102
+ );
103
+ }
104
+
105
+ const emailEditorOptions = { stylingMode: 'filled', placeholder: 'Email', mode: 'email' };
106
+ const passwordEditorOptions = { stylingMode: 'filled', placeholder: 'Password', mode: 'password' };
107
+ const confirmedPasswordEditorOptions = { stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' };
@@ -0,0 +1,12 @@
1
+ .footer {
2
+ display: block;
3
+ color: var(--base-text-color-alpha-7);
4
+ border-top: 1px solid var(--footer-border-color);
5
+ padding-top: 20px;
6
+ padding-bottom: 24px;
7
+ margin: 0 40px;
8
+
9
+ @media (max-width: 599.99px) {
10
+ margin: 0 20px;
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import './Footer.scss';
2
+
3
+ export default function Footer({ ...rest }) {
4
+ return <footer className={'footer'} {...rest} />;
5
+ }
@@ -0,0 +1,40 @@
1
+ @use "../../dx-styles.scss" as *;
2
+
3
+ header {
4
+ background-color: var(--base-bg);
5
+ }
6
+
7
+ .header-component {
8
+ flex: 0 0 auto;
9
+ z-index: 1;
10
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
11
+ }
12
+
13
+ .dx-toolbar.header-toolbar .dx-toolbar-items-container .dx-toolbar-after {
14
+ padding: 0 40px;
15
+
16
+ @media (max-width: 599.99px) {
17
+ padding: 0 20px;
18
+ }
19
+ }
20
+
21
+ .dx-toolbar .dx-toolbar-item.dx-toolbar-button.menu-button {
22
+ width: $side-panel-min-width;
23
+ text-align: center;
24
+ padding: 0;
25
+ }
26
+
27
+ .header-title .dx-item-content {
28
+ padding: 0;
29
+ margin: 0;
30
+ }
31
+
32
+ .dx-theme-generic {
33
+ .header-toolbar {
34
+ padding: 10px 0;
35
+ }
36
+
37
+ .user-button>.dx-button-content {
38
+ padding: 3px;
39
+ }
40
+ }
@@ -0,0 +1,38 @@
1
+ import Toolbar, { Item } from 'devextreme-react/toolbar';
2
+ import Button from 'devextreme-react/button';
3
+ import UserPanel from '@/components/user-panel/UserPanel';
4
+ import './Header.scss';
5
+ import { ThemeSwitcher } from '@/components/theme-switcher/ThemeSwitcher';
6
+ <%=#isTypeScript%>import type { HeaderProps } from '@/types';<%=/isTypeScript%>
7
+
8
+ const renderMenuItem = () => <UserPanel menuMode='list' />;
9
+
10
+ export default function Header({ menuToggleEnabled, title, toggleMenu }<%=#isTypeScript%>: HeaderProps<%=/isTypeScript%>) {
11
+ return (
12
+ <header className={'header-component'}>
13
+ <Toolbar className={'header-toolbar'}>
14
+ <Item
15
+ visible={menuToggleEnabled}
16
+ location={'before'}
17
+ widget={'dxButton'}
18
+ cssClass={'menu-button'}
19
+ >
20
+ <Button icon="menu" stylingMode="text" onClick={toggleMenu} />
21
+ </Item>
22
+ <Item
23
+ location={'before'}
24
+ cssClass={'header-title'}
25
+ text={title}
26
+ visible={!!title}
27
+ />
28
+ <Item
29
+ location={'after'}
30
+ >
31
+ <ThemeSwitcher />
32
+ </Item>
33
+ <Item location='after' locateInMenu='auto' menuItemRender={renderMenuItem}>
34
+ <UserPanel menuMode='context' />
35
+ </Item>
36
+ </Toolbar>
37
+ </header>
38
+ )}
@@ -0,0 +1,7 @@
1
+ export { default as Header } from './header/Header';
2
+ export { default as Footer } from './footer/Footer';
3
+ export { default as LoginForm } from './login-form/LoginForm';
4
+ export { default as ResetPasswordForm } from './reset-password-form/ResetPasswordForm';
5
+ export { default as ChangePasswordForm } from './change-password-form/ChangePasswordForm';
6
+ export { default as CreateAccountForm } from './create-account-form/CreateAccountForm';
7
+ export { default as SideNavigationMenu } from './side-navigation-menu/SideNavigationMenu';
@@ -0,0 +1,12 @@
1
+ .login-form {
2
+ .link {
3
+ text-align: center;
4
+ font-size: 12px;
5
+ font-style: normal;
6
+ margin: 6px 0 50px;
7
+ }
8
+
9
+ .form-text {
10
+ color: var(--base-text-color-alpha-7);
11
+ }
12
+ }