devextreme-cli 1.12.1 → 1.13.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/index.js CHANGED
@@ -41,7 +41,7 @@ const run = async(commands, options) => {
41
41
  await application.run(commands, options, devextremeConfig.read());
42
42
  } else if(application.isMigrationCommand(commands[0])) {
43
43
  if(!commands[1]) {
44
- console.error('Please specify a change name for migration.');
44
+ console.error('Please specify a migration command.');
45
45
  printHelp('migrate');
46
46
  return;
47
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devextreme-cli",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "DevExtreme CLI",
5
5
  "keywords": [
6
6
  "devexpress",
@@ -43,14 +43,13 @@
43
43
  "minimist": "^1.2.8",
44
44
  "mustache": "^3.2.1",
45
45
  "prompts": "^2.4.2",
46
- "sass": "^1.93.3",
46
+ "sass": "^1.97.1",
47
47
  "semver": "^5.7.2",
48
48
  "strip-bom": "^4.0.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@typescript-eslint/eslint-plugin": "^4.33.0",
52
52
  "@typescript-eslint/parser": "^4.33.0",
53
- "babel-eslint": "^10.1.0",
54
53
  "create-vite": "7.1.3",
55
54
  "cross-env": "^5.2.1",
56
55
  "eslint": "^7.32.0",
@@ -70,9 +69,13 @@
70
69
  "rimraf": "^2.7.1",
71
70
  "tree-kill": "^1.2.2",
72
71
  "tree-kill-promise": "^1.0.12",
73
- "typescript": "^4.0.2",
72
+ "typescript": "^4.9.5",
74
73
  "typescript-eslint-parser": "^22.0.0",
75
74
  "wait-on": "8.0.5"
76
75
  },
77
- "gitHead": "7af3cab84fb0dee433711d63dd8ed65af4c7d9dd"
76
+ "overrides": {
77
+ "@babel/traverse@<7.23.2": ">=7.23.2",
78
+ "braces@<3.0.3": ">=3.0.3"
79
+ },
80
+ "gitHead": "51bbc8d2fbfa40b741792c6921e27429cdb04c6f"
78
81
  }
@@ -19,7 +19,7 @@ const handleWrongAppType = (appType, command) => {
19
19
  };
20
20
 
21
21
  const handleWrongChangeName = (changeName, command) => {
22
- console.error(`The '${changeName}' change name is not valid`);
22
+ console.error(`Invalid migration command: ${changeName}`);
23
23
  printHelp(command);
24
24
  };
25
25
 
@@ -194,7 +194,7 @@ const migrateConfigComponents = async(options = {}) => {
194
194
  if(!collectionPath) {
195
195
  const prompts = require('prompts');
196
196
 
197
- console.log(`\nThe '${collectionName}' package is required to run this command.`);
197
+ console.log(`\nRequired package is missing: '${collectionName}'`);
198
198
 
199
199
  const response = await prompts({
200
200
  type: 'confirm',
@@ -204,7 +204,7 @@ const migrateConfigComponents = async(options = {}) => {
204
204
  });
205
205
 
206
206
  if(!response.install) {
207
- console.log('Migration cancelled. Install devextreme-schematics manually and rerun the command.');
207
+ console.log('Migration was canceled.');
208
208
  process.exit(1);
209
209
  }
210
210
  }
@@ -79,6 +79,7 @@ const create = async(appName, options) => {
79
79
  '--src-dir',
80
80
  '--app',
81
81
  '--no-turbopack',
82
+ '--no-react-compiler',
82
83
  '--import-alias "@/*"',
83
84
  ];
84
85
 
@@ -82,7 +82,7 @@ const create = async(appName, options) => {
82
82
 
83
83
  const commandArguments = [`-p=create-vite@${depsVersionTag || latestVersions['create-vite']}`, 'create-vite', appName];
84
84
 
85
- commandArguments.push(`--template react${transpiler === 'swc' ? '-swc' : ''}${templateOptions.isTypeScript ? '-ts' : ''}`);
85
+ commandArguments.push(`--template react${transpiler === 'swc' ? '-swc' : ''}${templateOptions.isTypeScript ? '-ts' : ''} --no-rolldown --no-interactive`);
86
86
 
87
87
  await runCommand('npx', commandArguments);
88
88
 
@@ -17,15 +17,10 @@ const defaultStyles = [
17
17
  const preparePackageJsonForTemplate = (appPath, appName) => {
18
18
  const dependencies = [
19
19
  { name: 'sass-embedded', version: '^1.85.1' },
20
- { name: 'vue-router', version: '^3.0.1' },
20
+ { name: 'vue-router', version: '^4.0.1' },
21
21
  { name: 'devextreme-cli', version: latestVersions['devextreme-cli'], dev: true }
22
22
  ];
23
23
 
24
- const nameDepends = dependencies.map(d => d.name);
25
- const indexVueRouter = nameDepends.indexOf('vue-router');
26
-
27
- dependencies[indexVueRouter].version = '^4.0.1';
28
-
29
24
  const scripts = [
30
25
  { name: 'build-themes', value: 'devextreme build' },
31
26
  { name: 'postinstall', value: 'npm run build-themes' }
package/src/commands.json CHANGED
@@ -51,19 +51,19 @@
51
51
  }, {
52
52
  "name": "migrate",
53
53
  "description": "Migration commands for DevExtreme applications",
54
- "usage": "devextreme migrate <change name> [options]",
54
+ "usage": "devextreme migrate <migration command> [options]",
55
55
  "arguments": [{
56
56
  "name": "angular-config-components",
57
- "description": "Migrate to the latest DevExtreme configuration components.",
57
+ "description": "Migrates DevExtreme Angular applications to named configuration components.",
58
58
  "options": [{
59
59
  "name": "--include",
60
- "description": "Template file glob patterns to include (default: **/*.html). You can pass multiple patterns as a comma-separated string (e.g. \"**/a.html,**/b.html\") or as an array (e.g. [\"**/a.html\",\"**/b.html\"])."
60
+ "description": "Template file glob patterns to include (default: **/*.html). You can pass multiple patterns as a comma-separated string (\"**/a.html,**/b.html\") or as an array ([\"**/a.html\",\"**/b.html\"])."
61
61
  }, {
62
62
  "name": "--script-include",
63
- "description": "TypeScript/JavaScript file glob patterns to scan for inline templates (default: **/*.ts,**/*.js). You can pass multiple patterns as a comma-separated string or as an array. Pass an empty value ('' or []) to disable."
63
+ "description": "TypeScript/JavaScript file glob patterns to scan for inline templates (default: **/*.ts,**/*.js). You can pass multiple patterns as a comma-separated string or as an array. Pass an empty value to disable ('' or [])."
64
64
  }, {
65
65
  "name": "--dry",
66
- "description": "Run in dry mode to preview changes without applying them (default: false)."
66
+ "description": "Run in dry mode to preview changes."
67
67
  }]
68
68
  }]
69
69
  }, {
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import Form, {
5
5
  Item,
@@ -7,7 +7,8 @@ import Form, {
7
7
  ButtonItem,
8
8
  ButtonOptions,
9
9
  RequiredRule,
10
- CustomRule,
10
+ CustomRule,<%=#isTypeScript%>
11
+ type FormTypes,<%=/isTypeScript%>
11
12
  } from 'devextreme-react/form';
12
13
  import LoadIndicator from 'devextreme-react/load-indicator';
13
14
  import notify from 'devextreme/ui/notify';
@@ -17,11 +18,22 @@ import { changePassword } from '@/app/actions/auth';
17
18
  export default function ChangePasswordForm() {
18
19
  const router = useRouter();
19
20
  const [loading, setLoading] = useState(false);
20
- const formData = useRef({ password: '' });
21
+ const [formData, setFormData] = useState({ password: '' });
22
+
23
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
24
+ const { dataField, value } = e;
25
+
26
+ if (dataField) {
27
+ setFormData(formData => ({
28
+ ...formData,
29
+ [dataField]: value,
30
+ }));
31
+ }
32
+ }, []);
21
33
 
22
34
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
23
35
  e.preventDefault();
24
- const { password } = formData.current;
36
+ const { password } = formData;
25
37
  setLoading(true);
26
38
 
27
39
  const result = await changePassword(password);
@@ -32,16 +44,16 @@ export default function ChangePasswordForm() {
32
44
  } else {
33
45
  notify(result.message, 'error', 2000);
34
46
  }
35
- }, [router]);
47
+ }, [router, formData]);
36
48
 
37
49
  const confirmPassword = useCallback(
38
- ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
39
- []
50
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.password,
51
+ [formData]
40
52
  );
41
53
 
42
54
  return (
43
55
  <form onSubmit={onSubmit}>
44
- <Form formData={formData.current} disabled={loading}>
56
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
45
57
  <Item
46
58
  dataField={'password'}
47
59
  editorType={'dxTextBox'}
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import Link from 'next/link';
5
5
  import Form, {
@@ -9,7 +9,8 @@ import Form, {
9
9
  ButtonOptions,
10
10
  RequiredRule,
11
11
  CustomRule,
12
- EmailRule
12
+ EmailRule,<%=#isTypeScript%>
13
+ type FormTypes,<%=/isTypeScript%>
13
14
  } from 'devextreme-react/form';
14
15
  import notify from 'devextreme/ui/notify';
15
16
  import LoadIndicator from 'devextreme-react/load-indicator';
@@ -20,11 +21,22 @@ import './CreateAccountForm.scss';
20
21
  export default function CreateAccountForm() {
21
22
  const router = useRouter();
22
23
  const [loading, setLoading] = useState(false);
23
- const formData = useRef({ email: '', password: '' });
24
+ const [formData, setFormData] = useState({ email: '', password: '' });
25
+
26
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
27
+ const { dataField, value } = e;
28
+
29
+ if (dataField) {
30
+ setFormData(formData => ({
31
+ ...formData,
32
+ [dataField]: value,
33
+ }));
34
+ }
35
+ }, []);
24
36
 
25
37
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
26
38
  e.preventDefault();
27
- const { email, password } = formData.current;
39
+ const { email, password } = formData;
28
40
  setLoading(true);
29
41
 
30
42
  const result = await signUp(email, password);
@@ -35,16 +47,16 @@ export default function CreateAccountForm() {
35
47
  } else {
36
48
  notify(result.message, 'error', 2000);
37
49
  }
38
- }, [router]);
50
+ }, [router, formData]);
39
51
 
40
52
  const confirmPassword = useCallback(
41
- ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
42
- []
53
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.password,
54
+ [formData]
43
55
  );
44
56
 
45
57
  return (
46
58
  <form className={'create-account-form'} onSubmit={onSubmit}>
47
- <Form formData={formData.current} disabled={loading}>
59
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
48
60
  <Item
49
61
  dataField={'email'}
50
62
  editorType={'dxTextBox'}
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import Link from 'next/link';
5
5
  import Form, {
@@ -8,7 +8,8 @@ import Form, {
8
8
  ButtonItem,
9
9
  ButtonOptions,
10
10
  RequiredRule,
11
- EmailRule
11
+ EmailRule,<%=#isTypeScript%>
12
+ type FormTypes,<%=/isTypeScript%>
12
13
  } from 'devextreme-react/form';
13
14
  import LoadIndicator from 'devextreme-react/load-indicator';
14
15
  import Button from 'devextreme-react/button';
@@ -20,11 +21,22 @@ import './LoginForm.scss';
20
21
  export default function LoginForm() {
21
22
  const router = useRouter();
22
23
  const [loading, setLoading] = useState(false);
23
- const formData = useRef({ email: '', password: '' });
24
+ const [formData, setFormData] = useState({ email: '', password: '' });
25
+
26
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
27
+ const { dataField, value } = e;
28
+
29
+ if (dataField) {
30
+ setFormData(formData => ({
31
+ ...formData,
32
+ [dataField]: value,
33
+ }));
34
+ }
35
+ }, []);
24
36
 
25
37
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
26
38
  e.preventDefault();
27
- const { email, password } = formData.current;
39
+ const { email, password } = formData;
28
40
  setLoading(true);
29
41
 
30
42
  const result = await signIn(email, password);
@@ -34,7 +46,7 @@ export default function LoginForm() {
34
46
  } else {
35
47
  router.push('/');
36
48
  }
37
- }, [router]);
49
+ }, [router, formData]);
38
50
 
39
51
  const onCreateAccountClick = useCallback(() => {
40
52
  router.push('/auth/create-account');
@@ -42,7 +54,7 @@ export default function LoginForm() {
42
54
 
43
55
  return (
44
56
  <form className={'login-form'} onSubmit={onSubmit}>
45
- <Form formData={formData.current} disabled={loading}>
57
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
46
58
  <Item
47
59
  dataField={'email'}
48
60
  editorType={'dxTextBox'}
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
2
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import Link from 'next/link';
5
5
  import Form, {
@@ -8,7 +8,8 @@ import Form, {
8
8
  ButtonItem,
9
9
  ButtonOptions,
10
10
  RequiredRule,
11
- EmailRule
11
+ EmailRule,<%=#isTypeScript%>
12
+ type FormTypes,<%=/isTypeScript%>
12
13
  } from 'devextreme-react/form';
13
14
  import LoadIndicator from 'devextreme-react/load-indicator';
14
15
  import notify from 'devextreme/ui/notify';
@@ -20,11 +21,22 @@ const notificationText = 'We\'ve sent a link to reset your password. Check your
20
21
  export default function ResetPasswordForm() {
21
22
  const router = useRouter();
22
23
  const [loading, setLoading] = useState(false);
23
- const formData = useRef({ email: '', password: '' });
24
+ const [formData, setFormData] = useState({ email: '', password: '' });
25
+
26
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
27
+ const { dataField, value } = e;
28
+
29
+ if (dataField) {
30
+ setFormData(formData => ({
31
+ ...formData,
32
+ [dataField]: value,
33
+ }));
34
+ }
35
+ }, []);
24
36
 
25
37
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
26
38
  e.preventDefault();
27
- const { email } = formData.current;
39
+ const { email } = formData;
28
40
  setLoading(true);
29
41
 
30
42
  const result = await resetPassword(email);
@@ -36,11 +48,11 @@ export default function ResetPasswordForm() {
36
48
  } else {
37
49
  notify(result.message, 'error', 2000);
38
50
  }
39
- }, [router]);
51
+ }, [router, formData]);
40
52
 
41
53
  return (
42
54
  <form className={'reset-password-form'} onSubmit={onSubmit}>
43
- <Form formData={formData.current} disabled={loading}>
55
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
44
56
  <Item
45
57
  dataField={'email'}
46
58
  editorType={'dxTextBox'}
@@ -1,8 +1,5 @@
1
1
  body {
2
2
  margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
- sans-serif;
6
3
  -moz-osx-font-smoothing: grayscale;
7
4
  }
8
5
 
@@ -19,7 +19,7 @@ async function _DEMO_logIn() {
19
19
  // return NextResponse.redirect(new URL('/auth/login', req.nextUrl))
20
20
  }
21
21
 
22
- export default async function middleware(req<%=#isTypeScript%>: NextRequest<%=/isTypeScript%>) {
22
+ export default async function proxy(req<%=#isTypeScript%>: NextRequest<%=/isTypeScript%>) {
23
23
  const path = req.nextUrl.pathname;
24
24
 
25
25
  if (!isProtectedRoute(path)) {
@@ -1,4 +1,4 @@
1
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
1
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
2
2
  import { useNavigate, useParams } from 'react-router-dom';
3
3
  import Form, {
4
4
  Item,
@@ -6,7 +6,8 @@ import Form, {
6
6
  ButtonItem,
7
7
  ButtonOptions,
8
8
  RequiredRule,
9
- CustomRule,
9
+ CustomRule,<%=#isTypeScript%>
10
+ type FormTypes,<%=/isTypeScript%>
10
11
  } from 'devextreme-react/form';
11
12
  import LoadIndicator from 'devextreme-react/load-indicator';
12
13
  import notify from 'devextreme/ui/notify';
@@ -16,12 +17,23 @@ import { changePassword } from '../../api/auth';
16
17
  export default function ChangePasswordForm() {
17
18
  const navigate = useNavigate();
18
19
  const [loading, setLoading] = useState(false);
19
- const formData = useRef({ password: '' });
20
+ const [formData, setFormData] = useState({ password: '' });
20
21
  const { recoveryCode } = useParams();
21
22
 
23
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
24
+ const { dataField, value } = e;
25
+
26
+ if (dataField) {
27
+ setFormData(formData => ({
28
+ ...formData,
29
+ [dataField]: value,
30
+ }));
31
+ }
32
+ }, []);
33
+
22
34
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
23
35
  e.preventDefault();
24
- const { password } = formData.current;
36
+ const { password } = formData;
25
37
  setLoading(true);
26
38
 
27
39
  const result = await changePassword(password, recoveryCode);
@@ -32,16 +44,16 @@ export default function ChangePasswordForm() {
32
44
  } else {
33
45
  notify(result.message, 'error', 2000);
34
46
  }
35
- }, [navigate, recoveryCode]);
47
+ }, [navigate, recoveryCode, formData]);
36
48
 
37
49
  const confirmPassword = useCallback(
38
- ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
39
- []
50
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.password,
51
+ [formData]
40
52
  );
41
53
 
42
54
  return (
43
55
  <form onSubmit={onSubmit}>
44
- <Form formData={formData.current} disabled={loading}>
56
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
45
57
  <Item
46
58
  dataField={'password'}
47
59
  editorType={'dxTextBox'}
@@ -1,4 +1,4 @@
1
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
1
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
2
2
  import { Link, useNavigate } from 'react-router-dom';
3
3
  import Form, {
4
4
  Item,
@@ -7,7 +7,8 @@ import Form, {
7
7
  ButtonOptions,
8
8
  RequiredRule,
9
9
  CustomRule,
10
- EmailRule
10
+ EmailRule,<%=#isTypeScript%>
11
+ type FormTypes,<%=/isTypeScript%>
11
12
  } from 'devextreme-react/form';
12
13
  import notify from 'devextreme/ui/notify';
13
14
  import LoadIndicator from 'devextreme-react/load-indicator';
@@ -18,11 +19,22 @@ import './CreateAccountForm.scss';
18
19
  export default function CreateAccountForm() {
19
20
  const navigate = useNavigate();
20
21
  const [loading, setLoading] = useState(false);
21
- const formData = useRef({ email: '', password: '' });
22
+ const [formData, setFormData] = useState({ email: '', password: '' });
23
+
24
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
25
+ const { dataField, value } = e;
26
+
27
+ if (dataField) {
28
+ setFormData(formData => ({
29
+ ...formData,
30
+ [dataField]: value,
31
+ }));
32
+ }
33
+ }, []);
22
34
 
23
35
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
24
36
  e.preventDefault();
25
- const { email, password } = formData.current;
37
+ const { email, password } = formData;
26
38
  setLoading(true);
27
39
 
28
40
  const result = await createAccount(email, password);
@@ -33,16 +45,16 @@ export default function CreateAccountForm() {
33
45
  } else {
34
46
  notify(result.message, 'error', 2000);
35
47
  }
36
- }, [navigate]);
48
+ }, [navigate, formData]);
37
49
 
38
50
  const confirmPassword = useCallback(
39
- ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password,
40
- []
51
+ ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.password,
52
+ [formData]
41
53
  );
42
54
 
43
55
  return (
44
56
  <form className={'create-account-form'} onSubmit={onSubmit}>
45
- <Form formData={formData.current} disabled={loading}>
57
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
46
58
  <Item
47
59
  dataField={'email'}
48
60
  editorType={'dxTextBox'}
@@ -1,4 +1,4 @@
1
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
1
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
2
2
  import { Link, useNavigate } from 'react-router-dom';
3
3
  import Form, {
4
4
  Item,
@@ -6,7 +6,8 @@ import Form, {
6
6
  ButtonItem,
7
7
  ButtonOptions,
8
8
  RequiredRule,
9
- EmailRule
9
+ EmailRule,<%=#isTypeScript%>
10
+ type FormTypes,<%=/isTypeScript%>
10
11
  } from 'devextreme-react/form';
11
12
  import LoadIndicator from 'devextreme-react/load-indicator';
12
13
  import Button from 'devextreme-react/button';
@@ -19,11 +20,22 @@ export default function LoginForm() {
19
20
  const navigate = useNavigate();
20
21
  const { signIn } = useAuth();
21
22
  const [loading, setLoading] = useState(false);
22
- const formData = useRef({ email: '', password: '' });
23
+ const [formData, setFormData] = useState({ email: '', password: '' });
24
+
25
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
26
+ const { dataField, value } = e;
27
+
28
+ if (dataField) {
29
+ setFormData(formData => ({
30
+ ...formData,
31
+ [dataField]: value,
32
+ }));
33
+ }
34
+ }, []);
23
35
 
24
36
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
25
37
  e.preventDefault();
26
- const { email, password } = formData.current;
38
+ const { email, password } = formData;
27
39
  setLoading(true);
28
40
 
29
41
  const result = await signIn(email, password);
@@ -31,7 +43,7 @@ export default function LoginForm() {
31
43
  setLoading(false);
32
44
  notify(result.message, 'error', 2000);
33
45
  }
34
- }, [signIn]);
46
+ }, [signIn, formData]);
35
47
 
36
48
  const onCreateAccountClick = useCallback(() => {
37
49
  navigate('/create-account');
@@ -39,7 +51,7 @@ export default function LoginForm() {
39
51
 
40
52
  return (
41
53
  <form className={'login-form'} onSubmit={onSubmit}>
42
- <Form formData={formData.current} disabled={loading}>
54
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
43
55
  <Item
44
56
  dataField={'email'}
45
57
  editorType={'dxTextBox'}
@@ -1,4 +1,4 @@
1
- import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react';
1
+ import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useCallback } from 'react';
2
2
  import { Link, useNavigate } from "react-router-dom";
3
3
  import Form, {
4
4
  Item,
@@ -6,7 +6,8 @@ import Form, {
6
6
  ButtonItem,
7
7
  ButtonOptions,
8
8
  RequiredRule,
9
- EmailRule
9
+ EmailRule,<%=#isTypeScript%>
10
+ type FormTypes,<%=/isTypeScript%>
10
11
  } from 'devextreme-react/form';
11
12
  import LoadIndicator from 'devextreme-react/load-indicator';
12
13
  import notify from 'devextreme/ui/notify';
@@ -18,11 +19,22 @@ const notificationText = 'We\'ve sent a link to reset your password. Check your
18
19
  export default function ResetPasswordForm() {
19
20
  const navigate = useNavigate();
20
21
  const [loading, setLoading] = useState(false);
21
- const formData = useRef({ email: '', password: '' });
22
+ const [formData, setFormData] = useState({ email: '', password: '' });
23
+
24
+ const onFieldDataChanged = useCallback((e<%=#isTypeScript%>: FormTypes.FieldDataChangedEvent<%=/isTypeScript%>) => {
25
+ const { dataField, value } = e;
26
+
27
+ if (dataField) {
28
+ setFormData(formData => ({
29
+ ...formData,
30
+ [dataField]: value,
31
+ }));
32
+ }
33
+ }, []);
22
34
 
23
35
  const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<HTMLFormElement><%=/isTypeScript%>) => {
24
36
  e.preventDefault();
25
- const { email } = formData.current;
37
+ const { email } = formData;
26
38
  setLoading(true);
27
39
 
28
40
  const result = await resetPassword(email);
@@ -34,11 +46,11 @@ export default function ResetPasswordForm() {
34
46
  } else {
35
47
  notify(result.message, 'error', 2000);
36
48
  }
37
- }, [navigate]);
49
+ }, [navigate, formData]);
38
50
 
39
51
  return (
40
52
  <form className={'reset-password-form'} onSubmit={onSubmit}>
41
- <Form formData={formData.current} disabled={loading}>
53
+ <Form formData={formData} disabled={loading} onFieldDataChanged={onFieldDataChanged}>
42
54
  <Item
43
55
  dataField={'email'}
44
56
  editorType={'dxTextBox'}
@@ -1,8 +1,5 @@
1
1
  body {
2
2
  margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
- sans-serif;
6
3
  -moz-osx-font-smoothing: grayscale;
7
4
  }
8
5
 
@@ -1,12 +1,12 @@
1
1
  const packageJson = require('../../package.json');
2
2
  module.exports = {
3
- 'devextreme': '25.1.6',
4
- 'devextreme-react': '25.1.6',
5
- 'devextreme-vue': '25.1.6',
6
- 'create-vite': '7.0.0',
3
+ 'devextreme': '25.2.3',
4
+ 'devextreme-react': '25.2.3',
5
+ 'devextreme-vue': '25.2.3',
6
+ 'create-vite': '8.2.0',
7
7
  'create-vue': '3.17.0',
8
- 'create-next-app': '15.3.4',
9
- 'jose': '6.0.11',
8
+ 'create-next-app': '16.1.0',
9
+ 'jose': '6.1.3',
10
10
  'devextreme-cli': packageJson.version,
11
11
  'devextreme-schematics': 'latest'
12
12
  };
@@ -2,12 +2,12 @@ const semver = require('semver').SemVer;
2
2
  const execSync = require('child_process').execSync;
3
3
 
4
4
  function parseNgCliVersion(stdout) {
5
- return /angular.cli:\s*(\S+)/ig.exec(stdout)[1];
5
+ return stdout.trim();
6
6
  }
7
7
 
8
8
  const getLocalNgVersion = () => {
9
9
  try {
10
- const version = parseNgCliVersion(execSync('ng v').toString());
10
+ const version = parseNgCliVersion(execSync('ng --version').toString());
11
11
  return new semver(version);
12
12
  } catch(e) {
13
13
  return '';