@khester/create-dynamics-app 2.0.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 (122) hide show
  1. package/README.md +28 -0
  2. package/dist/artifacts/registry.d.ts +4 -3
  3. package/dist/artifacts/registry.d.ts.map +1 -1
  4. package/dist/artifacts/registry.js +145 -11
  5. package/dist/artifacts/registry.js.map +1 -1
  6. package/dist/artifacts/types.d.ts +10 -1
  7. package/dist/artifacts/types.d.ts.map +1 -1
  8. package/dist/index.js +19 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/injectDevTools.d.ts.map +1 -1
  11. package/dist/injectDevTools.js +4 -2
  12. package/dist/injectDevTools.js.map +1 -1
  13. package/dist/scaffold.d.ts +23 -1
  14. package/dist/scaffold.d.ts.map +1 -1
  15. package/dist/scaffold.js +27 -1
  16. package/dist/scaffold.js.map +1 -1
  17. package/package.json +3 -2
  18. package/templates/grid-starter/ARCHITECTURE.md +66 -0
  19. package/templates/grid-starter/README.md +122 -0
  20. package/templates/grid-starter/env.example +16 -0
  21. package/templates/grid-starter/gitignore +6 -0
  22. package/templates/grid-starter/index.html +16 -0
  23. package/templates/grid-starter/package.json +39 -0
  24. package/templates/grid-starter/src/App.tsx +23 -0
  25. package/templates/grid-starter/src/core/services/FetchApiService.ts +117 -0
  26. package/templates/grid-starter/src/core/services/IApiService.ts +37 -0
  27. package/templates/grid-starter/src/core/services/MockApiService.ts +72 -0
  28. package/templates/grid-starter/src/core/services/ServiceFactory.ts +58 -0
  29. package/templates/grid-starter/src/core/services/XrmApiService.ts +135 -0
  30. package/templates/grid-starter/src/core/services/crudLogging.ts +52 -0
  31. package/templates/grid-starter/src/dev-tools/DevPanel.tsx +239 -0
  32. package/templates/grid-starter/src/grid/GridPage.tsx +119 -0
  33. package/templates/grid-starter/src/index.tsx +18 -0
  34. package/templates/grid-starter/src/vite-env.d.ts +15 -0
  35. package/templates/grid-starter/tools/deploy/deploy-webresource.cjs +117 -0
  36. package/templates/grid-starter/tsconfig.json +19 -0
  37. package/templates/grid-starter/vite.config.ts +76 -0
  38. package/templates/pcf-field/_variants/ValueInput.boolean.tsx +2 -0
  39. package/templates/pcf-field/_variants/ValueInput.date.tsx +2 -0
  40. package/templates/pcf-field/_variants/ValueInput.number.tsx +2 -0
  41. package/templates/pcf-field/_variants/ValueInput.optionset.tsx +77 -0
  42. package/templates/pcf-field/_variants/ValueInput.text.tsx +2 -0
  43. package/templates/pcf-field/index.ts +1 -1
  44. package/templates/pcf-field/package.json +3 -1
  45. package/templates/pcf-field/{{componentName}}Component.tsx +2 -0
  46. package/templates/react-custom-page/ARCHITECTURE.md +75 -0
  47. package/templates/react-custom-page/README.md +74 -568
  48. package/templates/react-custom-page/env.example +16 -0
  49. package/templates/react-custom-page/gitignore +1 -0
  50. package/templates/react-custom-page/index.html +16 -0
  51. package/templates/react-custom-page/package.json +21 -49
  52. package/templates/react-custom-page/src/App.tsx +26 -0
  53. package/templates/react-custom-page/src/core/recordContext.test.ts +30 -0
  54. package/templates/react-custom-page/src/core/recordContext.ts +51 -0
  55. package/templates/react-custom-page/src/core/services/FetchApiService.ts +117 -0
  56. package/templates/react-custom-page/src/core/services/IApiService.ts +37 -0
  57. package/templates/react-custom-page/src/core/services/MockApiService.ts +73 -0
  58. package/templates/react-custom-page/src/core/services/ServiceFactory.ts +58 -0
  59. package/templates/react-custom-page/src/core/services/XrmApiService.ts +135 -0
  60. package/templates/react-custom-page/src/core/services/crudLogging.ts +52 -0
  61. package/templates/react-custom-page/src/dev-tools/DevPanel.tsx +238 -0
  62. package/templates/react-custom-page/src/domain/diff.test.ts +87 -0
  63. package/templates/react-custom-page/src/domain/diff.ts +38 -0
  64. package/templates/react-custom-page/src/example/ExamplePage.tsx +140 -0
  65. package/templates/react-custom-page/src/example/exampleError.ts +36 -0
  66. package/templates/react-custom-page/src/example/hooks/useExampleData.ts +40 -0
  67. package/templates/react-custom-page/src/example/hooks/useExampleForm.ts +99 -0
  68. package/templates/react-custom-page/src/example/mappers/accountMapper.test.ts +38 -0
  69. package/templates/react-custom-page/src/example/mappers/accountMapper.ts +55 -0
  70. package/templates/react-custom-page/src/example/models/Account.ts +74 -0
  71. package/templates/react-custom-page/src/index.tsx +18 -128
  72. package/templates/react-custom-page/src/vite-env.d.ts +15 -0
  73. package/templates/react-custom-page/tools/deploy/deploy-webresource.cjs +117 -0
  74. package/templates/react-custom-page/tsconfig.json +12 -22
  75. package/templates/react-custom-page/vite.config.ts +76 -0
  76. package/templates/starter-page/README.md +38 -0
  77. package/templates/starter-page/_variants/App.dashboard.v8.tsx +46 -0
  78. package/templates/starter-page/_variants/App.form.v8.tsx +59 -0
  79. package/templates/starter-page/_variants/App.master-detail.v8.tsx +61 -0
  80. package/templates/starter-page/_variants/App.panel.v8.tsx +99 -0
  81. package/templates/starter-page/gitignore +5 -0
  82. package/templates/starter-page/package.json +27 -0
  83. package/templates/starter-page/public/index.html +11 -0
  84. package/templates/starter-page/src/index.tsx +10 -0
  85. package/templates/starter-page/src/services/dataverse.ts +30 -0
  86. package/templates/starter-page/tsconfig.json +15 -0
  87. package/templates/starter-page/webpack.config.js +17 -0
  88. package/templates/react-custom-page/deployment/README.md +0 -484
  89. package/templates/react-custom-page/docs/ARCHITECTURE_OVERVIEW.md +0 -506
  90. package/templates/react-custom-page/docs/BEST_PRACTICES.md +0 -723
  91. package/templates/react-custom-page/docs/MIGRATION_GUIDE.md +0 -447
  92. package/templates/react-custom-page/public/index.html +0 -15
  93. package/templates/react-custom-page/scripts/custom-build.js +0 -255
  94. package/templates/react-custom-page/src/components/AccountForm.css +0 -71
  95. package/templates/react-custom-page/src/components/AccountForm.tsx +0 -541
  96. package/templates/react-custom-page/src/components/AccountManagement.css +0 -86
  97. package/templates/react-custom-page/src/components/AccountManagement.tsx +0 -370
  98. package/templates/react-custom-page/src/components/ContactForm.css +0 -48
  99. package/templates/react-custom-page/src/components/ContactForm.tsx +0 -327
  100. package/templates/react-custom-page/src/components/ContactManagement.css +0 -86
  101. package/templates/react-custom-page/src/components/ContactManagement.tsx +0 -357
  102. package/templates/react-custom-page/src/components/Logging/LogDialog.tsx +0 -291
  103. package/templates/react-custom-page/src/components/Logging/LoggingContext.tsx +0 -166
  104. package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.css +0 -192
  105. package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.tsx +0 -177
  106. package/templates/react-custom-page/src/components/Logging/LoggingProvider.tsx +0 -3
  107. package/templates/react-custom-page/src/components/Logging/logger.ts +0 -193
  108. package/templates/react-custom-page/src/constants/account.ts +0 -410
  109. package/templates/react-custom-page/src/constants/contact.ts +0 -362
  110. package/templates/react-custom-page/src/models/Account.ts +0 -480
  111. package/templates/react-custom-page/src/models/BaseEntity.ts +0 -204
  112. package/templates/react-custom-page/src/models/Contact.ts +0 -580
  113. package/templates/react-custom-page/src/pcf/ContactControlWrapper.tsx +0 -107
  114. package/templates/react-custom-page/src/pcf/MultiEntityControlWrapper.tsx +0 -205
  115. package/templates/react-custom-page/src/providers/DynamicsProvider.tsx +0 -353
  116. package/templates/react-custom-page/src/services/MockApiService.ts +0 -260
  117. package/templates/react-custom-page/src/services/ServiceFactory.ts +0 -65
  118. package/templates/react-custom-page/src/services/XrmApiService.ts +0 -213
  119. package/templates/react-custom-page/src/styles/index.css +0 -171
  120. package/templates/react-custom-page/tools/metadata-sync/index.js +0 -152
  121. package/templates/react-custom-page/webpack.config.js +0 -57
  122. /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
- }