@khester/create-dynamics-app 2.1.0 → 2.2.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 (121) hide show
  1. package/dist/artifacts/registry.d.ts +4 -3
  2. package/dist/artifacts/registry.d.ts.map +1 -1
  3. package/dist/artifacts/registry.js +121 -11
  4. package/dist/artifacts/registry.js.map +1 -1
  5. package/dist/artifacts/types.d.ts +1 -1
  6. package/dist/artifacts/types.d.ts.map +1 -1
  7. package/dist/index.js +2 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/injectDevTools.d.ts.map +1 -1
  10. package/dist/injectDevTools.js +4 -2
  11. package/dist/injectDevTools.js.map +1 -1
  12. package/dist/scaffold.d.ts +1 -0
  13. package/dist/scaffold.d.ts.map +1 -1
  14. package/dist/scaffold.js +3 -1
  15. package/dist/scaffold.js.map +1 -1
  16. package/package.json +3 -2
  17. package/templates/grid-starter/ARCHITECTURE.md +66 -0
  18. package/templates/grid-starter/README.md +122 -0
  19. package/templates/grid-starter/env.example +16 -0
  20. package/templates/grid-starter/gitignore +6 -0
  21. package/templates/grid-starter/index.html +16 -0
  22. package/templates/grid-starter/package.json +39 -0
  23. package/templates/grid-starter/src/App.tsx +23 -0
  24. package/templates/grid-starter/src/core/services/FetchApiService.ts +117 -0
  25. package/templates/grid-starter/src/core/services/IApiService.ts +37 -0
  26. package/templates/grid-starter/src/core/services/MockApiService.ts +72 -0
  27. package/templates/grid-starter/src/core/services/ServiceFactory.ts +58 -0
  28. package/templates/grid-starter/src/core/services/XrmApiService.ts +135 -0
  29. package/templates/grid-starter/src/core/services/crudLogging.ts +52 -0
  30. package/templates/grid-starter/src/dev-tools/DevPanel.tsx +239 -0
  31. package/templates/grid-starter/src/grid/GridPage.tsx +119 -0
  32. package/templates/grid-starter/src/index.tsx +18 -0
  33. package/templates/grid-starter/src/vite-env.d.ts +15 -0
  34. package/templates/grid-starter/tools/deploy/deploy-webresource.cjs +117 -0
  35. package/templates/grid-starter/tsconfig.json +19 -0
  36. package/templates/grid-starter/vite.config.ts +76 -0
  37. package/templates/pcf-field/_variants/ValueInput.boolean.tsx +2 -0
  38. package/templates/pcf-field/_variants/ValueInput.date.tsx +2 -0
  39. package/templates/pcf-field/_variants/ValueInput.number.tsx +2 -0
  40. package/templates/pcf-field/_variants/ValueInput.optionset.tsx +77 -0
  41. package/templates/pcf-field/_variants/ValueInput.text.tsx +2 -0
  42. package/templates/pcf-field/index.ts +1 -1
  43. package/templates/pcf-field/package.json +3 -1
  44. package/templates/pcf-field/{{componentName}}Component.tsx +2 -0
  45. package/templates/react-custom-page/ARCHITECTURE.md +75 -0
  46. package/templates/react-custom-page/README.md +74 -568
  47. package/templates/react-custom-page/env.example +16 -0
  48. package/templates/react-custom-page/gitignore +1 -0
  49. package/templates/react-custom-page/index.html +16 -0
  50. package/templates/react-custom-page/package.json +21 -49
  51. package/templates/react-custom-page/src/App.tsx +26 -0
  52. package/templates/react-custom-page/src/core/recordContext.test.ts +30 -0
  53. package/templates/react-custom-page/src/core/recordContext.ts +51 -0
  54. package/templates/react-custom-page/src/core/services/FetchApiService.ts +117 -0
  55. package/templates/react-custom-page/src/core/services/IApiService.ts +37 -0
  56. package/templates/react-custom-page/src/core/services/MockApiService.ts +73 -0
  57. package/templates/react-custom-page/src/core/services/ServiceFactory.ts +58 -0
  58. package/templates/react-custom-page/src/core/services/XrmApiService.ts +135 -0
  59. package/templates/react-custom-page/src/core/services/crudLogging.ts +52 -0
  60. package/templates/react-custom-page/src/dev-tools/DevPanel.tsx +238 -0
  61. package/templates/react-custom-page/src/domain/diff.test.ts +87 -0
  62. package/templates/react-custom-page/src/domain/diff.ts +38 -0
  63. package/templates/react-custom-page/src/example/ExamplePage.tsx +140 -0
  64. package/templates/react-custom-page/src/example/exampleError.ts +36 -0
  65. package/templates/react-custom-page/src/example/hooks/useExampleData.ts +40 -0
  66. package/templates/react-custom-page/src/example/hooks/useExampleForm.ts +99 -0
  67. package/templates/react-custom-page/src/example/mappers/accountMapper.test.ts +38 -0
  68. package/templates/react-custom-page/src/example/mappers/accountMapper.ts +55 -0
  69. package/templates/react-custom-page/src/example/models/Account.ts +74 -0
  70. package/templates/react-custom-page/src/index.tsx +18 -128
  71. package/templates/react-custom-page/src/vite-env.d.ts +15 -0
  72. package/templates/react-custom-page/tools/deploy/deploy-webresource.cjs +117 -0
  73. package/templates/react-custom-page/tsconfig.json +12 -22
  74. package/templates/react-custom-page/vite.config.ts +76 -0
  75. package/templates/starter-page/README.md +38 -0
  76. package/templates/starter-page/_variants/App.dashboard.v8.tsx +46 -0
  77. package/templates/starter-page/_variants/App.form.v8.tsx +59 -0
  78. package/templates/starter-page/_variants/App.master-detail.v8.tsx +61 -0
  79. package/templates/starter-page/_variants/App.panel.v8.tsx +99 -0
  80. package/templates/starter-page/gitignore +5 -0
  81. package/templates/starter-page/package.json +27 -0
  82. package/templates/starter-page/public/index.html +11 -0
  83. package/templates/starter-page/src/index.tsx +10 -0
  84. package/templates/starter-page/src/services/dataverse.ts +30 -0
  85. package/templates/starter-page/tsconfig.json +15 -0
  86. package/templates/starter-page/webpack.config.js +17 -0
  87. package/templates/react-custom-page/deployment/README.md +0 -484
  88. package/templates/react-custom-page/docs/ARCHITECTURE_OVERVIEW.md +0 -506
  89. package/templates/react-custom-page/docs/BEST_PRACTICES.md +0 -723
  90. package/templates/react-custom-page/docs/MIGRATION_GUIDE.md +0 -447
  91. package/templates/react-custom-page/public/index.html +0 -15
  92. package/templates/react-custom-page/scripts/custom-build.js +0 -255
  93. package/templates/react-custom-page/src/components/AccountForm.css +0 -71
  94. package/templates/react-custom-page/src/components/AccountForm.tsx +0 -541
  95. package/templates/react-custom-page/src/components/AccountManagement.css +0 -86
  96. package/templates/react-custom-page/src/components/AccountManagement.tsx +0 -370
  97. package/templates/react-custom-page/src/components/ContactForm.css +0 -48
  98. package/templates/react-custom-page/src/components/ContactForm.tsx +0 -327
  99. package/templates/react-custom-page/src/components/ContactManagement.css +0 -86
  100. package/templates/react-custom-page/src/components/ContactManagement.tsx +0 -357
  101. package/templates/react-custom-page/src/components/Logging/LogDialog.tsx +0 -291
  102. package/templates/react-custom-page/src/components/Logging/LoggingContext.tsx +0 -166
  103. package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.css +0 -192
  104. package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.tsx +0 -177
  105. package/templates/react-custom-page/src/components/Logging/LoggingProvider.tsx +0 -3
  106. package/templates/react-custom-page/src/components/Logging/logger.ts +0 -193
  107. package/templates/react-custom-page/src/constants/account.ts +0 -410
  108. package/templates/react-custom-page/src/constants/contact.ts +0 -362
  109. package/templates/react-custom-page/src/models/Account.ts +0 -480
  110. package/templates/react-custom-page/src/models/BaseEntity.ts +0 -204
  111. package/templates/react-custom-page/src/models/Contact.ts +0 -580
  112. package/templates/react-custom-page/src/pcf/ContactControlWrapper.tsx +0 -107
  113. package/templates/react-custom-page/src/pcf/MultiEntityControlWrapper.tsx +0 -205
  114. package/templates/react-custom-page/src/providers/DynamicsProvider.tsx +0 -353
  115. package/templates/react-custom-page/src/services/MockApiService.ts +0 -260
  116. package/templates/react-custom-page/src/services/ServiceFactory.ts +0 -65
  117. package/templates/react-custom-page/src/services/XrmApiService.ts +0 -213
  118. package/templates/react-custom-page/src/styles/index.css +0 -171
  119. package/templates/react-custom-page/tools/metadata-sync/index.js +0 -152
  120. package/templates/react-custom-page/webpack.config.js +0 -57
  121. /package/templates/_shared/dev-tools/auth/{get-token.js → get-token.cjs} +0 -0
@@ -1,71 +0,0 @@
1
- .account-form {
2
- padding: 16px 0;
3
- }
4
-
5
- .account-form__content {
6
- margin-bottom: 24px;
7
- }
8
-
9
- .account-form__row {
10
- display: grid;
11
- grid-template-columns: 1fr 1fr;
12
- gap: 16px;
13
- margin-bottom: 16px;
14
- }
15
-
16
- .account-form__field {
17
- display: flex;
18
- flex-direction: column;
19
- }
20
-
21
- .account-form__field--full {
22
- grid-column: 1 / -1;
23
- }
24
-
25
- .account-form__section {
26
- margin: 24px 0;
27
- padding: 16px;
28
- border: 1px solid #edebe9;
29
- border-radius: 4px;
30
- background-color: #faf9f8;
31
- }
32
-
33
- .account-form__section h3 {
34
- margin: 0 0 16px 0;
35
- font-size: 16px;
36
- font-weight: 600;
37
- color: #323130;
38
- }
39
-
40
- .account-form__actions {
41
- display: flex;
42
- gap: 12px;
43
- justify-content: flex-start;
44
- padding-top: 16px;
45
- border-top: 1px solid #edebe9;
46
- }
47
-
48
- .account-form__error {
49
- padding: 12px 16px;
50
- margin-bottom: 16px;
51
- background-color: #fef7f1;
52
- border: 1px solid #d13438;
53
- border-radius: 4px;
54
- color: #d13438;
55
- font-weight: 500;
56
- }
57
-
58
- @media (max-width: 768px) {
59
- .account-form__row {
60
- grid-template-columns: 1fr;
61
- gap: 12px;
62
- }
63
-
64
- .account-form__actions {
65
- flex-direction: column;
66
- }
67
-
68
- .account-form__section {
69
- padding: 12px;
70
- }
71
- }
@@ -1,541 +0,0 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
2
- import { Button, TextField, Dropdown } from '@khester/dynamics-ui-components';
3
- import { useDynamicsApi } from '../providers/DynamicsProvider';
4
- import { Account, IAccount } from '../models/Account';
5
- import {
6
- AccountConstants,
7
- PreferredContactMethodCode_OptionSet,
8
- IndustryCode_OptionSet,
9
- } from '../constants/account';
10
- import { Logger } from './Logging/logger';
11
- import './AccountForm.css';
12
-
13
- interface AccountFormData {
14
- name: string;
15
- accountnumber: string;
16
- emailaddress1: string;
17
- telephone1: string;
18
- websiteurl: string;
19
- description: string;
20
- revenue: number | null;
21
- numberofemployees: number | null;
22
- industrycode: number;
23
- preferredcontactmethodcode: number;
24
- address1_line1: string;
25
- address1_city: string;
26
- address1_stateorprovince: string;
27
- address1_postalcode: string;
28
- address1_country: string;
29
- }
30
-
31
- interface AccountFormProps {
32
- accountId?: string;
33
- initialData?: Partial<AccountFormData>;
34
- onSave?: () => void;
35
- onCancel?: () => void;
36
- }
37
-
38
- const preferredContactOptions = [
39
- { key: PreferredContactMethodCode_OptionSet.Any, text: 'Any' },
40
- { key: PreferredContactMethodCode_OptionSet.Email, text: 'Email' },
41
- { key: PreferredContactMethodCode_OptionSet.Phone, text: 'Phone' },
42
- { key: PreferredContactMethodCode_OptionSet.Fax, text: 'Fax' },
43
- { key: PreferredContactMethodCode_OptionSet.Mail, text: 'Mail' },
44
- ];
45
-
46
- const industryOptions = [
47
- { key: 0, text: 'Not Specified' },
48
- { key: IndustryCode_OptionSet.Accounting, text: 'Accounting' },
49
- { key: IndustryCode_OptionSet.BusinessServices, text: 'Business Services' },
50
- { key: IndustryCode_OptionSet.Consulting, text: 'Consulting' },
51
- { key: IndustryCode_OptionSet.Financial, text: 'Financial' },
52
- { key: IndustryCode_OptionSet.Insurance, text: 'Insurance' },
53
- { key: IndustryCode_OptionSet.LegalServices, text: 'Legal Services' },
54
- { key: IndustryCode_OptionSet.DurableManufacturing, text: 'Manufacturing' },
55
- { key: IndustryCode_OptionSet.NonDurableMerchandiseRetail, text: 'Retail' },
56
- { key: IndustryCode_OptionSet.Transportation, text: 'Transportation' },
57
- { key: IndustryCode_OptionSet.Wholesale, text: 'Wholesale' },
58
- ];
59
-
60
- export const AccountForm: React.FC<AccountFormProps> = ({
61
- accountId,
62
- initialData,
63
- onSave,
64
- onCancel,
65
- }) => {
66
- const { apiService } = useDynamicsApi();
67
-
68
- const [formData, setFormData] = useState<AccountFormData>({
69
- name: '',
70
- accountnumber: '',
71
- emailaddress1: '',
72
- telephone1: '',
73
- websiteurl: '',
74
- description: '',
75
- revenue: null,
76
- numberofemployees: null,
77
- industrycode: 0,
78
- preferredcontactmethodcode: PreferredContactMethodCode_OptionSet.Any,
79
- address1_line1: '',
80
- address1_city: '',
81
- address1_stateorprovince: '',
82
- address1_postalcode: '',
83
- address1_country: '',
84
- });
85
-
86
- useEffect(() => {
87
- Logger.userAction(
88
- accountId
89
- ? 'Account form opened for edit'
90
- : 'Account form opened for new account',
91
- { accountId },
92
- 'AccountForm'
93
- );
94
- }, [accountId]);
95
-
96
- const [errors, setErrors] = useState<
97
- Partial<Record<keyof AccountFormData, string>>
98
- >({});
99
- const [isSubmitting, setIsSubmitting] = useState(false);
100
- const [submitError, setSubmitError] = useState<string>('');
101
-
102
- useEffect(() => {
103
- if (initialData) {
104
- Logger.debug(
105
- 'Populating form with initial data',
106
- 'AccountForm.useEffect',
107
- initialData
108
- );
109
-
110
- setFormData({
111
- name: initialData.name || '',
112
- accountnumber: initialData.accountnumber || '',
113
- emailaddress1: initialData.emailaddress1 || '',
114
- telephone1: initialData.telephone1 || '',
115
- websiteurl: initialData.websiteurl || '',
116
- description: initialData.description || '',
117
- revenue: initialData.revenue ?? null,
118
- numberofemployees: initialData.numberofemployees ?? null,
119
- industrycode: initialData.industrycode || 0,
120
- preferredcontactmethodcode:
121
- initialData.preferredcontactmethodcode ||
122
- PreferredContactMethodCode_OptionSet.Any,
123
- address1_line1: initialData.address1_line1 || '',
124
- address1_city: initialData.address1_city || '',
125
- address1_stateorprovince: initialData.address1_stateorprovince || '',
126
- address1_postalcode: initialData.address1_postalcode || '',
127
- address1_country: initialData.address1_country || '',
128
- });
129
- }
130
- }, [initialData]);
131
-
132
- const validateForm = useCallback((): boolean => {
133
- const newErrors: Partial<Record<keyof AccountFormData, string>> = {};
134
- const validationErrors: string[] = [];
135
-
136
- // Account name is required
137
- if (!formData.name.trim()) {
138
- const error = 'Account name is required';
139
- newErrors.name = error;
140
- validationErrors.push(error);
141
- } else if (formData.name.length > 160) {
142
- const error = 'Account name cannot exceed 160 characters';
143
- newErrors.name = error;
144
- validationErrors.push(error);
145
- }
146
-
147
- // Account number validation
148
- if (formData.accountnumber && formData.accountnumber.length > 20) {
149
- const error = 'Account number cannot exceed 20 characters';
150
- newErrors.accountnumber = error;
151
- validationErrors.push(error);
152
- }
153
-
154
- // Email validation
155
- if (
156
- formData.emailaddress1 &&
157
- !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.emailaddress1)
158
- ) {
159
- const error = 'Please enter a valid email address';
160
- newErrors.emailaddress1 = error;
161
- validationErrors.push(error);
162
- }
163
-
164
- // Phone validation
165
- if (formData.telephone1 && !/^\+?[\d\s\-()]+$/.test(formData.telephone1)) {
166
- const error = 'Please enter a valid phone number';
167
- newErrors.telephone1 = error;
168
- validationErrors.push(error);
169
- }
170
-
171
- // Website URL validation
172
- if (formData.websiteurl && formData.websiteurl.trim()) {
173
- try {
174
- new URL(formData.websiteurl);
175
- } catch {
176
- const error = 'Please enter a valid website URL';
177
- newErrors.websiteurl = error;
178
- validationErrors.push(error);
179
- }
180
- }
181
-
182
- // Revenue validation
183
- if (formData.revenue !== null && formData.revenue < 0) {
184
- const error = 'Revenue cannot be negative';
185
- newErrors.revenue = error;
186
- validationErrors.push(error);
187
- }
188
-
189
- // Number of employees validation
190
- if (formData.numberofemployees !== null && formData.numberofemployees < 0) {
191
- const error = 'Number of employees cannot be negative';
192
- newErrors.numberofemployees = error;
193
- validationErrors.push(error);
194
- }
195
-
196
- if (validationErrors.length > 0) {
197
- Logger.validation(
198
- 'Account',
199
- validationErrors,
200
- 'AccountForm.validateForm'
201
- );
202
- }
203
-
204
- setErrors(newErrors);
205
- return Object.keys(newErrors).length === 0;
206
- }, [formData]);
207
-
208
- const handleInputChange = useCallback(
209
- (field: keyof AccountFormData, value: any) => {
210
- setFormData((prev) => ({ ...prev, [field]: value }));
211
-
212
- // Clear error for this field when user starts typing
213
- if (errors[field]) {
214
- setErrors((prev) => ({ ...prev, [field]: undefined }));
215
- }
216
- setSubmitError('');
217
- },
218
- [errors]
219
- );
220
-
221
- const handleSubmit = useCallback(async () => {
222
- if (!validateForm()) {
223
- return;
224
- }
225
-
226
- if (!apiService) {
227
- setSubmitError('API service not available');
228
- Logger.error('API service not available', 'AccountForm.handleSubmit');
229
- return;
230
- }
231
-
232
- setIsSubmitting(true);
233
- setSubmitError('');
234
-
235
- try {
236
- Logger.userAction(
237
- accountId ? 'Account update submitted' : 'Account creation submitted',
238
- { accountId, formData },
239
- 'AccountForm.handleSubmit'
240
- );
241
-
242
- const accountData: IAccount = {
243
- [AccountConstants.PrimaryName]: formData.name.trim(),
244
- [AccountConstants.AccountNumber]:
245
- formData.accountnumber.trim() || undefined,
246
- [AccountConstants.EMailAddress1]:
247
- formData.emailaddress1.trim() || undefined,
248
- [AccountConstants.Telephone1]: formData.telephone1.trim() || undefined,
249
- [AccountConstants.WebSiteURL]: formData.websiteurl.trim() || undefined,
250
- [AccountConstants.Description]:
251
- formData.description.trim() || undefined,
252
- [AccountConstants.Revenue]: formData.revenue,
253
- [AccountConstants.NumberOfEmployees]: formData.numberofemployees,
254
- [AccountConstants.IndustryCode]:
255
- formData.industrycode > 0 ? formData.industrycode : undefined,
256
- [AccountConstants.PreferredContactMethodCode]:
257
- formData.preferredcontactmethodcode,
258
- [AccountConstants.Address1_Line1]:
259
- formData.address1_line1.trim() || undefined,
260
- [AccountConstants.Address1_City]:
261
- formData.address1_city.trim() || undefined,
262
- [AccountConstants.Address1_StateOrProvince]:
263
- formData.address1_stateorprovince.trim() || undefined,
264
- [AccountConstants.Address1_PostalCode]:
265
- formData.address1_postalcode.trim() || undefined,
266
- [AccountConstants.Address1_Country]:
267
- formData.address1_country.trim() || undefined,
268
- name: '',
269
- };
270
-
271
- // Remove undefined values
272
- Object.keys(accountData).forEach((key) => {
273
- if (accountData[key as keyof IAccount] === undefined) {
274
- delete accountData[key as keyof IAccount];
275
- }
276
- });
277
-
278
- const account = new Account(accountData);
279
-
280
- if (accountId) {
281
- account.accountid = accountId;
282
- await Account.update(apiService, account);
283
- Logger.log(
284
- `Successfully updated account: ${account.name}`,
285
- 'AccountForm.handleSubmit'
286
- );
287
- } else {
288
- await Account.create(apiService, account);
289
- Logger.log(
290
- `Successfully created account: ${account.name}`,
291
- 'AccountForm.handleSubmit'
292
- );
293
- }
294
-
295
- if (onSave) {
296
- onSave();
297
- }
298
- } catch (error) {
299
- const errorMessage =
300
- error instanceof Error
301
- ? error.message
302
- : 'An error occurred while saving';
303
- setSubmitError(errorMessage);
304
- Logger.error(
305
- `Failed to ${accountId ? 'update' : 'create'} account`,
306
- 'AccountForm.handleSubmit',
307
- error
308
- );
309
- } finally {
310
- setIsSubmitting(false);
311
- }
312
- }, [formData, accountId, validateForm, apiService, onSave]);
313
-
314
- const handleCancel = useCallback(() => {
315
- if (onCancel) {
316
- onCancel();
317
- }
318
- }, [onCancel]);
319
-
320
- return (
321
- <div className="account-form">
322
- {submitError && (
323
- <div className="account-form__error" role="alert">
324
- {submitError}
325
- </div>
326
- )}
327
-
328
- <div className="account-form__content">
329
- <div className="account-form__row">
330
- <div className="account-form__field">
331
- <TextField
332
- label="Account Name"
333
- required
334
- value={formData.name}
335
- onChange={(_, value) => handleInputChange('name', value || '')}
336
- errorMessage={errors.name}
337
- disabled={isSubmitting}
338
- />
339
- </div>
340
- <div className="account-form__field">
341
- <TextField
342
- label="Account Number"
343
- value={formData.accountnumber}
344
- onChange={(_, value) =>
345
- handleInputChange('accountnumber', value || '')
346
- }
347
- errorMessage={errors.accountnumber}
348
- disabled={isSubmitting}
349
- />
350
- </div>
351
- </div>
352
-
353
- <div className="account-form__row">
354
- <div className="account-form__field">
355
- <TextField
356
- label="Email Address"
357
- type="email"
358
- value={formData.emailaddress1}
359
- onChange={(_, value) =>
360
- handleInputChange('emailaddress1', value || '')
361
- }
362
- errorMessage={errors.emailaddress1}
363
- disabled={isSubmitting}
364
- />
365
- </div>
366
- <div className="account-form__field">
367
- <TextField
368
- label="Phone Number"
369
- type="tel"
370
- value={formData.telephone1}
371
- onChange={(_, value) =>
372
- handleInputChange('telephone1', value || '')
373
- }
374
- errorMessage={errors.telephone1}
375
- disabled={isSubmitting}
376
- />
377
- </div>
378
- </div>
379
-
380
- <div className="account-form__row">
381
- <div className="account-form__field">
382
- <TextField
383
- label="Website URL"
384
- type="url"
385
- value={formData.websiteurl}
386
- onChange={(_, value) =>
387
- handleInputChange('websiteurl', value || '')
388
- }
389
- errorMessage={errors.websiteurl}
390
- disabled={isSubmitting}
391
- />
392
- </div>
393
- <div className="account-form__field">
394
- <Dropdown
395
- label="Industry"
396
- options={industryOptions}
397
- selectedKey={formData.industrycode}
398
- onChange={(_, option) =>
399
- handleInputChange('industrycode', option?.key)
400
- }
401
- disabled={isSubmitting}
402
- />
403
- </div>
404
- </div>
405
-
406
- <div className="account-form__row">
407
- <div className="account-form__field">
408
- <TextField
409
- label="Revenue"
410
- type="number"
411
- value={formData.revenue?.toString() || ''}
412
- onChange={(_, value) =>
413
- handleInputChange(
414
- 'revenue',
415
- value ? parseFloat(value) || null : null
416
- )
417
- }
418
- errorMessage={errors.revenue}
419
- disabled={isSubmitting}
420
- />
421
- </div>
422
- <div className="account-form__field">
423
- <TextField
424
- label="Number of Employees"
425
- type="number"
426
- value={formData.numberofemployees?.toString() || ''}
427
- onChange={(_, value) =>
428
- handleInputChange(
429
- 'numberofemployees',
430
- value ? parseInt(value) || null : null
431
- )
432
- }
433
- errorMessage={errors.numberofemployees}
434
- disabled={isSubmitting}
435
- />
436
- </div>
437
- </div>
438
-
439
- <div className="account-form__row">
440
- <div className="account-form__field account-form__field--full">
441
- <TextField
442
- label="Description"
443
- multiline
444
- rows={3}
445
- value={formData.description}
446
- onChange={(_, value) =>
447
- handleInputChange('description', value || '')
448
- }
449
- disabled={isSubmitting}
450
- />
451
- </div>
452
- </div>
453
-
454
- <div className="account-form__section">
455
- <h3>Address Information</h3>
456
- <div className="account-form__row">
457
- <div className="account-form__field account-form__field--full">
458
- <TextField
459
- label="Street Address"
460
- value={formData.address1_line1}
461
- onChange={(_, value) =>
462
- handleInputChange('address1_line1', value || '')
463
- }
464
- disabled={isSubmitting}
465
- />
466
- </div>
467
- </div>
468
-
469
- <div className="account-form__row">
470
- <div className="account-form__field">
471
- <TextField
472
- label="City"
473
- value={formData.address1_city}
474
- onChange={(_, value) =>
475
- handleInputChange('address1_city', value || '')
476
- }
477
- disabled={isSubmitting}
478
- />
479
- </div>
480
- <div className="account-form__field">
481
- <TextField
482
- label="State/Province"
483
- value={formData.address1_stateorprovince}
484
- onChange={(_, value) =>
485
- handleInputChange('address1_stateorprovince', value || '')
486
- }
487
- disabled={isSubmitting}
488
- />
489
- </div>
490
- </div>
491
-
492
- <div className="account-form__row">
493
- <div className="account-form__field">
494
- <TextField
495
- label="Postal Code"
496
- value={formData.address1_postalcode}
497
- onChange={(_, value) =>
498
- handleInputChange('address1_postalcode', value || '')
499
- }
500
- disabled={isSubmitting}
501
- />
502
- </div>
503
- <div className="account-form__field">
504
- <TextField
505
- label="Country"
506
- value={formData.address1_country}
507
- onChange={(_, value) =>
508
- handleInputChange('address1_country', value || '')
509
- }
510
- disabled={isSubmitting}
511
- />
512
- </div>
513
- </div>
514
- </div>
515
-
516
- <div className="account-form__row">
517
- <div className="account-form__field">
518
- <Dropdown
519
- label="Preferred Contact Method"
520
- options={preferredContactOptions}
521
- selectedKey={formData.preferredcontactmethodcode}
522
- onChange={(_, option) =>
523
- handleInputChange('preferredcontactmethodcode', option?.key)
524
- }
525
- disabled={isSubmitting}
526
- />
527
- </div>
528
- </div>
529
- </div>
530
-
531
- <div className="account-form__actions">
532
- <Button
533
- text={accountId ? 'Update' : 'Save'}
534
- onClick={handleSubmit}
535
- disabled={isSubmitting}
536
- />
537
- <Button text="Cancel" onClick={handleCancel} disabled={isSubmitting} />
538
- </div>
539
- </div>
540
- );
541
- };
@@ -1,86 +0,0 @@
1
- .account-management {
2
- padding: 16px;
3
- }
4
-
5
- .account-management__header {
6
- display: flex;
7
- justify-content: space-between;
8
- align-items: center;
9
- margin-bottom: 16px;
10
- flex-wrap: wrap;
11
- gap: 16px;
12
- }
13
-
14
- .account-management__header h2 {
15
- margin: 0;
16
- font-size: 20px;
17
- font-weight: 600;
18
- color: #323130;
19
- }
20
-
21
- .account-management__actions {
22
- display: flex;
23
- gap: 12px;
24
- align-items: center;
25
- flex-wrap: wrap;
26
- }
27
-
28
- .account-management__toolbar {
29
- display: flex;
30
- gap: 8px;
31
- margin-bottom: 16px;
32
- padding: 8px 0;
33
- border-bottom: 1px solid #edebe9;
34
- }
35
-
36
- .account-management__list {
37
- min-height: 400px;
38
- border: 1px solid #edebe9;
39
- border-radius: 4px;
40
- }
41
-
42
- .account-management__loading {
43
- display: flex;
44
- justify-content: center;
45
- align-items: center;
46
- height: 200px;
47
- color: #605e5c;
48
- font-style: italic;
49
- }
50
-
51
- .account-management__empty {
52
- display: flex;
53
- justify-content: center;
54
- align-items: center;
55
- height: 200px;
56
- color: #605e5c;
57
- font-style: italic;
58
- text-align: center;
59
- padding: 16px;
60
- }
61
-
62
- .account-management__dialog-actions {
63
- display: flex;
64
- gap: 12px;
65
- justify-content: flex-end;
66
- padding-top: 16px;
67
- }
68
-
69
- @media (max-width: 768px) {
70
- .account-management {
71
- padding: 8px;
72
- }
73
-
74
- .account-management__header {
75
- flex-direction: column;
76
- align-items: stretch;
77
- }
78
-
79
- .account-management__actions {
80
- justify-content: stretch;
81
- }
82
-
83
- .account-management__toolbar {
84
- flex-wrap: wrap;
85
- }
86
- }