@khester/create-dynamics-app 1.0.8 → 1.1.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 (107) hide show
  1. package/bin/create-dynamics-app.js +1 -1
  2. package/dist/index.js +140 -15
  3. package/dist/index.js.map +1 -1
  4. package/dist/utils/consultingHelpers.d.ts +13 -0
  5. package/dist/utils/consultingHelpers.d.ts.map +1 -0
  6. package/dist/utils/consultingHelpers.js +569 -0
  7. package/dist/utils/consultingHelpers.js.map +1 -0
  8. package/dist/utils/copyTemplate.d.ts.map +1 -1
  9. package/dist/utils/copyTemplate.js.map +1 -1
  10. package/dist/utils/initGit.d.ts.map +1 -1
  11. package/dist/utils/initGit.js.map +1 -1
  12. package/dist/utils/installDependencies.d.ts.map +1 -1
  13. package/dist/utils/installDependencies.js +3 -2
  14. package/dist/utils/installDependencies.js.map +1 -1
  15. package/dist/utils/updatePackageJson.d.ts +1 -1
  16. package/dist/utils/updatePackageJson.d.ts.map +1 -1
  17. package/dist/utils/updatePackageJson.js +11 -1
  18. package/dist/utils/updatePackageJson.js.map +1 -1
  19. package/package.json +1 -1
  20. package/templates/dynamics-365-starter/INTEGRATION_TEST_RESULTS.md +302 -0
  21. package/templates/dynamics-365-starter/PHASE_4_COMPLETION_SUMMARY.md +305 -0
  22. package/templates/dynamics-365-starter/README.md +566 -137
  23. package/templates/dynamics-365-starter/deployment/QUICKSTART-MAC.md +507 -0
  24. package/templates/dynamics-365-starter/deployment/QUICKSTART-WINDOWS.md +372 -0
  25. package/templates/dynamics-365-starter/deployment/README.md +484 -0
  26. package/templates/dynamics-365-starter/deployment/pipelines/README.md +375 -0
  27. package/templates/dynamics-365-starter/deployment/pipelines/azure-pipelines.yml +330 -0
  28. package/templates/dynamics-365-starter/deployment/pipelines/github-actions.yml +422 -0
  29. package/templates/dynamics-365-starter/deployment/pipelines/jenkins.groovy +636 -0
  30. package/templates/dynamics-365-starter/deployment/scripts/deploy.ps1 +417 -0
  31. package/templates/dynamics-365-starter/deployment/scripts/deploy.sh +582 -0
  32. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.ps1 +486 -0
  33. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.sh +567 -0
  34. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.ps1 +703 -0
  35. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.sh +671 -0
  36. package/templates/dynamics-365-starter/docs/ARCHITECTURE_OVERVIEW.md +506 -0
  37. package/templates/dynamics-365-starter/docs/BEST_PRACTICES.md +723 -0
  38. package/templates/dynamics-365-starter/docs/MIGRATION_GUIDE.md +447 -0
  39. package/templates/dynamics-365-starter/docs/team-standards/README.md +273 -0
  40. package/templates/dynamics-365-starter/docs/team-standards/client-onboarding.md +577 -0
  41. package/templates/dynamics-365-starter/docs/team-standards/code-review-checklist.md +359 -0
  42. package/templates/dynamics-365-starter/docs/team-standards/coding-standards.md +700 -0
  43. package/templates/dynamics-365-starter/docs/team-standards/cross-platform-team-guide.md +736 -0
  44. package/templates/dynamics-365-starter/docs/team-standards/development-workflows.md +727 -0
  45. package/templates/dynamics-365-starter/docs/troubleshooting/common-errors.md +758 -0
  46. package/templates/dynamics-365-starter/docs/troubleshooting/platform-specific-issues.md +878 -0
  47. package/templates/dynamics-365-starter/package.json +22 -1
  48. package/templates/dynamics-365-starter/public/index.html +8 -11
  49. package/templates/dynamics-365-starter/scripts/custom-build.js +255 -0
  50. package/templates/dynamics-365-starter/src/client-project-template/README.md +234 -0
  51. package/templates/dynamics-365-starter/src/client-project-template/config/client.template.json +114 -0
  52. package/templates/dynamics-365-starter/src/client-project-template/config/environments/template.json +186 -0
  53. package/templates/dynamics-365-starter/src/client-project-template/scripts/client-setup.js +667 -0
  54. package/templates/dynamics-365-starter/src/components/AccountForm.css +71 -0
  55. package/templates/dynamics-365-starter/src/components/AccountForm.tsx +541 -0
  56. package/templates/dynamics-365-starter/src/components/AccountManagement.css +86 -0
  57. package/templates/dynamics-365-starter/src/components/AccountManagement.tsx +370 -0
  58. package/templates/dynamics-365-starter/src/components/ContactForm.tsx +149 -63
  59. package/templates/dynamics-365-starter/src/components/ContactManagement.tsx +153 -63
  60. package/templates/dynamics-365-starter/src/components/Logging/LogDialog.tsx +291 -0
  61. package/templates/dynamics-365-starter/src/components/Logging/LoggingContext.tsx +166 -0
  62. package/templates/dynamics-365-starter/src/components/Logging/LoggingDebugPanel.css +192 -0
  63. package/templates/dynamics-365-starter/src/components/Logging/LoggingDebugPanel.tsx +177 -0
  64. package/templates/dynamics-365-starter/src/components/Logging/LoggingProvider.tsx +3 -0
  65. package/templates/dynamics-365-starter/src/components/Logging/logger.ts +193 -0
  66. package/templates/dynamics-365-starter/src/constants/account.ts +410 -0
  67. package/templates/dynamics-365-starter/src/constants/contact.ts +362 -0
  68. package/templates/dynamics-365-starter/src/examples/README.md +52 -0
  69. package/templates/dynamics-365-starter/src/examples/component-examples/opportunity-management.tsx +625 -0
  70. package/templates/dynamics-365-starter/src/examples/entity-examples/opportunity-model.ts +545 -0
  71. package/templates/dynamics-365-starter/src/examples/integration-examples/custom-pcf-wrapper.tsx +722 -0
  72. package/templates/dynamics-365-starter/src/examples/workflow-examples/sales-workflow.ts +662 -0
  73. package/templates/dynamics-365-starter/src/index.tsx +107 -19
  74. package/templates/dynamics-365-starter/src/models/Account.ts +480 -0
  75. package/templates/dynamics-365-starter/src/models/BaseEntity.ts +204 -0
  76. package/templates/dynamics-365-starter/src/models/Contact.ts +580 -0
  77. package/templates/dynamics-365-starter/src/page-templates/EntityDashboard.tsx +519 -0
  78. package/templates/dynamics-365-starter/src/page-templates/EntityDetailPage.tsx +456 -0
  79. package/templates/dynamics-365-starter/src/page-templates/EntityListPage.tsx +406 -0
  80. package/templates/dynamics-365-starter/src/page-templates/RelatedEntitiesPage.tsx +578 -0
  81. package/templates/dynamics-365-starter/src/page-templates/SearchPage.tsx +629 -0
  82. package/templates/dynamics-365-starter/src/pcf/ContactControlWrapper.tsx +75 -22
  83. package/templates/dynamics-365-starter/src/pcf/MultiEntityControlWrapper.tsx +205 -0
  84. package/templates/dynamics-365-starter/src/providers/DynamicsProvider.tsx +297 -80
  85. package/templates/dynamics-365-starter/src/services/MockApiService.ts +260 -0
  86. package/templates/dynamics-365-starter/src/services/ServiceFactory.ts +65 -0
  87. package/templates/dynamics-365-starter/src/services/XrmApiService.ts +213 -0
  88. package/templates/dynamics-365-starter/src/styles/index.css +74 -7
  89. package/templates/dynamics-365-starter/tools/entity-generator/index.js +168 -0
  90. package/templates/dynamics-365-starter/tools/entity-generator/templates/constants.template.ts +124 -0
  91. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.css +283 -0
  92. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.tsx +275 -0
  93. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.css +204 -0
  94. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.tsx +413 -0
  95. package/templates/dynamics-365-starter/tools/entity-generator/templates/model.template.ts +250 -0
  96. package/templates/dynamics-365-starter/tools/metadata-sync/d365-client.js +410 -0
  97. package/templates/dynamics-365-starter/tools/metadata-sync/index.js +512 -0
  98. package/templates/dynamics-365-starter/tools/metadata-sync/type-generator.js +675 -0
  99. package/templates/dynamics-365-starter/tsconfig.json +11 -8
  100. package/templates/dynamics-365-starter/webpack.config.js +8 -9
  101. package/templates/power-pages-starter/README.md +7 -1
  102. package/templates/power-pages-starter/public/index.html +8 -11
  103. package/templates/power-pages-starter/src/components/ContactForm.tsx +60 -41
  104. package/templates/power-pages-starter/src/index.tsx +3 -3
  105. package/templates/power-pages-starter/src/providers/PowerPagesProvider.tsx +46 -23
  106. package/templates/power-pages-starter/tsconfig.json +3 -9
  107. package/templates/power-pages-starter/webpack.config.js +8 -3
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { ContactManagement } from '../components/ContactManagement';
3
3
  import { DynamicsProvider } from '../providers/DynamicsProvider';
4
+ import { ServiceFactory } from '../services/ServiceFactory';
5
+ import { Logger } from '../components/Logging/logger';
4
6
 
5
7
  interface PCFContextType {
6
8
  // Define PCF context properties based on your needs
@@ -17,32 +19,83 @@ interface ContactControlWrapperProps {
17
19
  * Wrapper component for integrating ContactManagement with PCF (PowerApps Component Framework)
18
20
  * This allows the component to be used as a custom control in Dynamics 365 forms and views
19
21
  */
20
- export const ContactControlWrapper: React.FC<ContactControlWrapperProps> = ({ context }) => {
22
+ export const ContactControlWrapper: React.FC<ContactControlWrapperProps> = ({
23
+ context,
24
+ }) => {
21
25
  // Extract configuration from PCF context
22
26
  const baseUrl = context.parameters?.baseUrl?.raw || '';
23
-
24
- // Create a custom API service that uses PCF's webAPI
25
- const createPCFApiService = () => ({
26
- createRecord: async (entityName: string, data: any) => {
27
- return await context.webAPI.createRecord(entityName, data);
28
- },
29
- retrieveRecord: async (entityName: string, id: string, select?: string) => {
30
- return await context.webAPI.retrieveRecord(entityName, id, select);
31
- },
32
- updateRecord: async (entityName: string, id: string, data: any) => {
33
- return await context.webAPI.updateRecord(entityName, id, data);
34
- },
35
- deleteRecord: async (entityName: string, id: string) => {
36
- return await context.webAPI.deleteRecord(entityName, id);
37
- },
38
- retrieveMultiple: async (entityName: string, query?: string) => {
39
- const fetchXml = query || `<fetch><entity name="${entityName}"/></fetch>`;
40
- return await context.webAPI.retrieveMultipleRecords(entityName, `?fetchXml=${encodeURIComponent(fetchXml)}`);
27
+
28
+ // Create API service using ServiceFactory for environment detection
29
+ const createPCFApiService = () => {
30
+ try {
31
+ // Check if we're in PCF context with webAPI available
32
+ if (context.webAPI) {
33
+ Logger.log(
34
+ 'PCF WebAPI available, creating custom PCF API service',
35
+ 'ContactControlWrapper'
36
+ );
37
+
38
+ // Create a custom API service that uses PCF's webAPI
39
+ return {
40
+ createRecord: async (entityName: string, data: any) => {
41
+ return await context.webAPI.createRecord(entityName, data);
42
+ },
43
+ retrieveRecord: async (
44
+ entityName: string,
45
+ id: string,
46
+ select?: string
47
+ ) => {
48
+ return await context.webAPI.retrieveRecord(entityName, id, select);
49
+ },
50
+ updateRecord: async (entityName: string, id: string, data: any) => {
51
+ return await context.webAPI.updateRecord(entityName, id, data);
52
+ },
53
+ deleteRecord: async (entityName: string, id: string) => {
54
+ return await context.webAPI.deleteRecord(entityName, id);
55
+ },
56
+ retrieveMultipleRecords: async (
57
+ entityName: string,
58
+ fetchXml: string
59
+ ) => {
60
+ return await context.webAPI.retrieveMultipleRecords(
61
+ entityName,
62
+ `?fetchXml=${encodeURIComponent(fetchXml)}`
63
+ );
64
+ },
65
+ executeRequest: async (requestName: string, _requestData: any) => {
66
+ Logger.log(
67
+ `Executing request: ${requestName}`,
68
+ 'ContactControlWrapper.executeRequest'
69
+ );
70
+ throw new Error('Custom requests not supported in PCF WebAPI');
71
+ },
72
+ uploadFile: async (_file: File) => {
73
+ Logger.log(
74
+ 'File upload requested',
75
+ 'ContactControlWrapper.uploadFile'
76
+ );
77
+ throw new Error('File upload not supported in PCF WebAPI');
78
+ },
79
+ };
80
+ }
81
+
82
+ // Fallback to ServiceFactory for environment detection
83
+ const xrmGlobal = ServiceFactory.isDynamics365Context()
84
+ ? (window as any).Xrm
85
+ : undefined;
86
+ return ServiceFactory.createApiService(xrmGlobal);
87
+ } catch (error) {
88
+ Logger.error(
89
+ 'Failed to create API service in PCF context',
90
+ 'ContactControlWrapper',
91
+ error
92
+ );
93
+ throw error;
41
94
  }
42
- });
95
+ };
43
96
 
44
97
  return (
45
- <DynamicsProvider baseUrl={baseUrl}>
98
+ <DynamicsProvider customApiService={createPCFApiService()}>
46
99
  <div style={{ width: '100%', height: '100%' }}>
47
100
  <ContactManagement />
48
101
  </div>
@@ -51,4 +104,4 @@ export const ContactControlWrapper: React.FC<ContactControlWrapperProps> = ({ co
51
104
  };
52
105
 
53
106
  // Export for PCF integration
54
- export default ContactControlWrapper;
107
+ export default ContactControlWrapper;
@@ -0,0 +1,205 @@
1
+ import React, { useState, useCallback, useEffect } from 'react';
2
+ import { Pivot, DynamicsPivotItemProps } from '@khester/dynamics-ui-components';
3
+ import { ContactManagement } from '../components/ContactManagement';
4
+ import { AccountManagement } from '../components/AccountManagement';
5
+ import { DynamicsProvider } from '../providers/DynamicsProvider';
6
+ import { ServiceFactory } from '../services/ServiceFactory';
7
+ import { Logger } from '../components/Logging/logger';
8
+
9
+ interface PCFContextType {
10
+ // Define PCF context properties based on your needs
11
+ webAPI: any;
12
+ utils: any;
13
+ parameters: any;
14
+ }
15
+
16
+ interface MultiEntityControlWrapperProps {
17
+ context: PCFContextType;
18
+ defaultEntity?: 'contact' | 'account';
19
+ showTabs?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Multi-entity wrapper component for integrating both ContactManagement and AccountManagement
24
+ * with PCF (PowerApps Component Framework). This allows switching between entity management
25
+ * within a single custom control in Dynamics 365.
26
+ */
27
+ export const MultiEntityControlWrapper: React.FC<
28
+ MultiEntityControlWrapperProps
29
+ > = ({ context, defaultEntity = 'contact', showTabs = true }) => {
30
+ const [selectedEntity, setSelectedEntity] = useState<'contact' | 'account'>(
31
+ defaultEntity
32
+ );
33
+
34
+ // Extract configuration from PCF context
35
+ const baseUrl = context.parameters?.baseUrl?.raw || '';
36
+
37
+ useEffect(() => {
38
+ Logger.userAction(
39
+ 'MultiEntityControlWrapper initialized',
40
+ { defaultEntity, showTabs, selectedEntity },
41
+ 'MultiEntityControlWrapper'
42
+ );
43
+ }, [defaultEntity, showTabs, selectedEntity]);
44
+
45
+ // Create API service using ServiceFactory for environment detection
46
+ const createPCFApiService = () => {
47
+ try {
48
+ // Check if we're in PCF context with webAPI available
49
+ if (context.webAPI) {
50
+ Logger.log(
51
+ 'PCF WebAPI available, creating custom PCF API service',
52
+ 'MultiEntityControlWrapper'
53
+ );
54
+
55
+ // Create a custom API service that uses PCF's webAPI
56
+ return {
57
+ createRecord: async (entityName: string, data: any) => {
58
+ Logger.debug(
59
+ `Creating ${entityName} record`,
60
+ 'MultiEntityControlWrapper.createRecord',
61
+ data
62
+ );
63
+ return await context.webAPI.createRecord(entityName, data);
64
+ },
65
+ retrieveRecord: async (
66
+ entityName: string,
67
+ id: string,
68
+ select?: string
69
+ ) => {
70
+ Logger.debug(
71
+ `Retrieving ${entityName} record: ${id}`,
72
+ 'MultiEntityControlWrapper.retrieveRecord'
73
+ );
74
+ return await context.webAPI.retrieveRecord(entityName, id, select);
75
+ },
76
+ updateRecord: async (entityName: string, id: string, data: any) => {
77
+ Logger.debug(
78
+ `Updating ${entityName} record: ${id}`,
79
+ 'MultiEntityControlWrapper.updateRecord',
80
+ data
81
+ );
82
+ return await context.webAPI.updateRecord(entityName, id, data);
83
+ },
84
+ deleteRecord: async (entityName: string, id: string) => {
85
+ Logger.debug(
86
+ `Deleting ${entityName} record: ${id}`,
87
+ 'MultiEntityControlWrapper.deleteRecord'
88
+ );
89
+ return await context.webAPI.deleteRecord(entityName, id);
90
+ },
91
+ retrieveMultipleRecords: async (
92
+ entityName: string,
93
+ fetchXml: string
94
+ ) => {
95
+ Logger.debug(
96
+ `Retrieving multiple ${entityName} records`,
97
+ 'MultiEntityControlWrapper.retrieveMultipleRecords'
98
+ );
99
+ return await context.webAPI.retrieveMultipleRecords(
100
+ entityName,
101
+ `?fetchXml=${encodeURIComponent(fetchXml)}`
102
+ );
103
+ },
104
+ executeRequest: async (requestName: string, _requestData: any) => {
105
+ Logger.log(
106
+ `Executing request: ${requestName}`,
107
+ 'MultiEntityControlWrapper.executeRequest'
108
+ );
109
+ throw new Error('Custom requests not supported in PCF WebAPI');
110
+ },
111
+ uploadFile: async (_file: File) => {
112
+ Logger.log(
113
+ 'File upload requested',
114
+ 'MultiEntityControlWrapper.uploadFile'
115
+ );
116
+ throw new Error('File upload not supported in PCF WebAPI');
117
+ },
118
+ };
119
+ }
120
+
121
+ // Fallback to ServiceFactory for environment detection
122
+ const xrmGlobal = ServiceFactory.isDynamics365Context()
123
+ ? (window as any).Xrm
124
+ : undefined;
125
+ return ServiceFactory.createApiService(xrmGlobal);
126
+ } catch (error) {
127
+ Logger.error(
128
+ 'Failed to create API service in PCF context',
129
+ 'MultiEntityControlWrapper',
130
+ error
131
+ );
132
+ throw error;
133
+ }
134
+ };
135
+
136
+ const handleEntityChange = useCallback(
137
+ (item: DynamicsPivotItemProps) => {
138
+ const newEntity = item.itemKey as 'contact' | 'account';
139
+ Logger.userAction(
140
+ 'Entity tab changed',
141
+ { from: selectedEntity, to: newEntity },
142
+ 'MultiEntityControlWrapper.handleEntityChange'
143
+ );
144
+ setSelectedEntity(newEntity);
145
+ },
146
+ [selectedEntity]
147
+ );
148
+
149
+ const renderEntityManagement = () => {
150
+ switch (selectedEntity) {
151
+ case 'contact':
152
+ return <ContactManagement />;
153
+ case 'account':
154
+ return <AccountManagement />;
155
+ default:
156
+ return <ContactManagement />;
157
+ }
158
+ };
159
+
160
+ const containerStyle: React.CSSProperties = {
161
+ width: '100%',
162
+ height: '100%',
163
+ display: 'flex',
164
+ flexDirection: 'column',
165
+ };
166
+
167
+ const contentStyle: React.CSSProperties = {
168
+ flex: 1,
169
+ overflow: 'auto',
170
+ };
171
+
172
+ return (
173
+ <DynamicsProvider customApiService={createPCFApiService()}>
174
+ <div style={containerStyle}>
175
+ {showTabs ? (
176
+ <>
177
+ <Pivot
178
+ items={[
179
+ {
180
+ headerText: 'Contacts',
181
+ itemKey: 'contact',
182
+ itemIcon: 'Contact',
183
+ },
184
+ {
185
+ headerText: 'Accounts',
186
+ itemKey: 'account',
187
+ itemIcon: 'Building',
188
+ },
189
+ ]}
190
+ selectedKey={selectedEntity}
191
+ onItemClick={handleEntityChange}
192
+ linkFormat="tabs"
193
+ />
194
+ <div style={contentStyle}>{renderEntityManagement()}</div>
195
+ </>
196
+ ) : (
197
+ <div style={contentStyle}>{renderEntityManagement()}</div>
198
+ )}
199
+ </div>
200
+ </DynamicsProvider>
201
+ );
202
+ };
203
+
204
+ // Export for PCF integration
205
+ export default MultiEntityControlWrapper;