@khester/create-dynamics-app 1.1.0 → 2.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 (210) hide show
  1. package/README.md +74 -0
  2. package/dist/artifacts/registry.d.ts +18 -0
  3. package/dist/artifacts/registry.d.ts.map +1 -0
  4. package/dist/artifacts/registry.js +340 -0
  5. package/dist/artifacts/registry.js.map +1 -0
  6. package/dist/artifacts/types.d.ts +122 -0
  7. package/dist/artifacts/types.d.ts.map +1 -0
  8. package/dist/artifacts/types.js +7 -0
  9. package/dist/artifacts/types.js.map +1 -0
  10. package/dist/artifacts/validators.d.ts +16 -0
  11. package/dist/artifacts/validators.d.ts.map +1 -0
  12. package/dist/artifacts/validators.js +45 -0
  13. package/dist/artifacts/validators.js.map +1 -0
  14. package/dist/fromDesign.d.ts +5 -0
  15. package/dist/fromDesign.d.ts.map +1 -0
  16. package/dist/fromDesign.js +98 -0
  17. package/dist/fromDesign.js.map +1 -0
  18. package/dist/index.js +129 -177
  19. package/dist/index.js.map +1 -1
  20. package/dist/injectDevTools.d.ts +28 -0
  21. package/dist/injectDevTools.d.ts.map +1 -0
  22. package/dist/injectDevTools.js +148 -0
  23. package/dist/injectDevTools.js.map +1 -0
  24. package/dist/scaffold.d.ts +48 -0
  25. package/dist/scaffold.d.ts.map +1 -0
  26. package/dist/scaffold.js +180 -0
  27. package/dist/scaffold.js.map +1 -0
  28. package/dist/templatePlan.d.ts +3 -0
  29. package/dist/templatePlan.d.ts.map +1 -0
  30. package/dist/templatePlan.js +43 -0
  31. package/dist/templatePlan.js.map +1 -0
  32. package/dist/utils/copyTemplate.d.ts +13 -1
  33. package/dist/utils/copyTemplate.d.ts.map +1 -1
  34. package/dist/utils/copyTemplate.js +98 -4
  35. package/dist/utils/copyTemplate.js.map +1 -1
  36. package/dist/utils/updatePackageJson.d.ts +11 -1
  37. package/dist/utils/updatePackageJson.d.ts.map +1 -1
  38. package/dist/utils/updatePackageJson.js +12 -10
  39. package/dist/utils/updatePackageJson.js.map +1 -1
  40. package/package.json +10 -7
  41. package/templates/_shared/dev-tools/auth/get-token.js +72 -0
  42. package/templates/_shared/dev-tools/dev/mock-xrm.js +42 -0
  43. package/templates/_shared/dev-tools/metadata-sync/index.js +152 -0
  44. package/templates/_shared/dev-tools/smoke/test-retrieve.js +44 -0
  45. package/templates/dialog-form/README.md +27 -0
  46. package/templates/dialog-form/_variants/App.v8.tsx +39 -0
  47. package/templates/dialog-form/_variants/App.v9.tsx +41 -0
  48. package/templates/dialog-form/gitignore +5 -0
  49. package/templates/dialog-form/package.json +27 -0
  50. package/templates/dialog-form/public/index.html +11 -0
  51. package/templates/dialog-form/src/index.tsx +10 -0
  52. package/templates/dialog-form/src/services/dataverse.ts +30 -0
  53. package/templates/dialog-form/tsconfig.json +15 -0
  54. package/templates/dialog-form/webpack.config.js +17 -0
  55. package/templates/grid-customizer/README.md +28 -0
  56. package/templates/grid-customizer/gitignore +4 -0
  57. package/templates/grid-customizer/package.json +25 -0
  58. package/templates/grid-customizer/src/GridCustomizer.ts +28 -0
  59. package/templates/grid-customizer/src/cell-renderers.tsx +35 -0
  60. package/templates/grid-customizer/src/index.ts +4 -0
  61. package/templates/grid-customizer/src/types/grid-types.ts +30 -0
  62. package/templates/grid-customizer/src/utils/color-utils.ts +24 -0
  63. package/templates/grid-customizer/tsconfig.json +15 -0
  64. package/templates/grid-customizer/webpack.config.js +17 -0
  65. package/templates/pcf-dataset/ControlManifest.Input.xml +16 -0
  66. package/templates/pcf-dataset/README.md +21 -0
  67. package/templates/pcf-dataset/gitignore +5 -0
  68. package/templates/pcf-dataset/index.ts +39 -0
  69. package/templates/pcf-dataset/package.json +30 -0
  70. package/templates/pcf-dataset/strings/{{componentName}}.1033.resx +47 -0
  71. package/templates/pcf-dataset/tsconfig.json +8 -0
  72. package/templates/pcf-dataset/{{componentName}}Component.tsx +39 -0
  73. package/templates/pcf-field/ControlManifest.Input.xml +17 -0
  74. package/templates/pcf-field/README.md +95 -0
  75. package/templates/pcf-field/_variants/ValueInput.boolean.tsx +24 -0
  76. package/templates/pcf-field/_variants/ValueInput.date.tsx +27 -0
  77. package/templates/pcf-field/_variants/ValueInput.number.tsx +35 -0
  78. package/templates/pcf-field/_variants/ValueInput.text.tsx +27 -0
  79. package/templates/pcf-field/gitignore +5 -0
  80. package/templates/pcf-field/index.ts +61 -0
  81. package/templates/pcf-field/package.json +30 -0
  82. package/templates/pcf-field/strings/{{componentName}}.1033.resx +47 -0
  83. package/templates/pcf-field/tsconfig.json +8 -0
  84. package/templates/pcf-field/{{componentName}}Component.tsx +35 -0
  85. package/templates/power-pages-starter/gitignore +5 -0
  86. package/templates/react-custom-page/gitignore +5 -0
  87. package/templates/{dynamics-365-starter → react-custom-page}/package.json +3 -3
  88. package/templates/react-custom-page/tools/metadata-sync/index.js +152 -0
  89. package/templates/static-web-app/README.md +36 -0
  90. package/templates/static-web-app/_variants/App.v8.tsx +32 -0
  91. package/templates/static-web-app/_variants/App.v9.tsx +31 -0
  92. package/templates/static-web-app/api/host.json +12 -0
  93. package/templates/static-web-app/api/package.json +19 -0
  94. package/templates/static-web-app/api/src/functions/hello.ts +16 -0
  95. package/templates/static-web-app/api/tsconfig.json +14 -0
  96. package/templates/static-web-app/frontend/index.html +12 -0
  97. package/templates/static-web-app/frontend/package.json +23 -0
  98. package/templates/static-web-app/frontend/src/index.tsx +8 -0
  99. package/templates/static-web-app/frontend/tsconfig.json +16 -0
  100. package/templates/static-web-app/frontend/vite.config.ts +13 -0
  101. package/templates/static-web-app/gitignore +8 -0
  102. package/templates/static-web-app/package.json +15 -0
  103. package/templates/static-web-app/staticwebapp.config.json +7 -0
  104. package/templates/teams-app/README.md +27 -0
  105. package/templates/teams-app/_variants/graph.off.ts +7 -0
  106. package/templates/teams-app/_variants/graph.on.ts +22 -0
  107. package/templates/teams-app/appPackage/manifest.json +26 -0
  108. package/templates/teams-app/gitignore +5 -0
  109. package/templates/teams-app/index.html +12 -0
  110. package/templates/teams-app/package.json +26 -0
  111. package/templates/teams-app/src/App.tsx +25 -0
  112. package/templates/teams-app/src/index.tsx +8 -0
  113. package/templates/teams-app/tsconfig.json +16 -0
  114. package/templates/teams-app/vite.config.ts +9 -0
  115. package/templates/web-resource/README.md +39 -0
  116. package/templates/web-resource/_variants/App.v8.tsx +29 -0
  117. package/templates/web-resource/_variants/App.v9.tsx +28 -0
  118. package/templates/web-resource/gitignore +5 -0
  119. package/templates/web-resource/package.json +27 -0
  120. package/templates/web-resource/public/index.html +11 -0
  121. package/templates/web-resource/src/index.tsx +10 -0
  122. package/templates/web-resource/src/services/dataverse.ts +30 -0
  123. package/templates/web-resource/tsconfig.json +15 -0
  124. package/templates/web-resource/webpack.config.js +17 -0
  125. package/dist/utils/consultingHelpers.d.ts +0 -13
  126. package/dist/utils/consultingHelpers.d.ts.map +0 -1
  127. package/dist/utils/consultingHelpers.js +0 -569
  128. package/dist/utils/consultingHelpers.js.map +0 -1
  129. package/templates/dynamics-365-starter/INTEGRATION_TEST_RESULTS.md +0 -302
  130. package/templates/dynamics-365-starter/PHASE_4_COMPLETION_SUMMARY.md +0 -305
  131. package/templates/dynamics-365-starter/deployment/QUICKSTART-MAC.md +0 -507
  132. package/templates/dynamics-365-starter/deployment/QUICKSTART-WINDOWS.md +0 -372
  133. package/templates/dynamics-365-starter/deployment/pipelines/README.md +0 -375
  134. package/templates/dynamics-365-starter/deployment/pipelines/azure-pipelines.yml +0 -330
  135. package/templates/dynamics-365-starter/deployment/pipelines/github-actions.yml +0 -422
  136. package/templates/dynamics-365-starter/deployment/pipelines/jenkins.groovy +0 -636
  137. package/templates/dynamics-365-starter/deployment/scripts/deploy.ps1 +0 -417
  138. package/templates/dynamics-365-starter/deployment/scripts/deploy.sh +0 -582
  139. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.ps1 +0 -486
  140. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.sh +0 -567
  141. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.ps1 +0 -703
  142. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.sh +0 -671
  143. package/templates/dynamics-365-starter/docs/team-standards/README.md +0 -273
  144. package/templates/dynamics-365-starter/docs/team-standards/client-onboarding.md +0 -577
  145. package/templates/dynamics-365-starter/docs/team-standards/code-review-checklist.md +0 -359
  146. package/templates/dynamics-365-starter/docs/team-standards/coding-standards.md +0 -700
  147. package/templates/dynamics-365-starter/docs/team-standards/cross-platform-team-guide.md +0 -736
  148. package/templates/dynamics-365-starter/docs/team-standards/development-workflows.md +0 -727
  149. package/templates/dynamics-365-starter/docs/troubleshooting/common-errors.md +0 -758
  150. package/templates/dynamics-365-starter/docs/troubleshooting/platform-specific-issues.md +0 -878
  151. package/templates/dynamics-365-starter/src/client-project-template/README.md +0 -234
  152. package/templates/dynamics-365-starter/src/client-project-template/config/client.template.json +0 -114
  153. package/templates/dynamics-365-starter/src/client-project-template/config/environments/template.json +0 -186
  154. package/templates/dynamics-365-starter/src/client-project-template/scripts/client-setup.js +0 -667
  155. package/templates/dynamics-365-starter/src/examples/README.md +0 -52
  156. package/templates/dynamics-365-starter/src/examples/component-examples/opportunity-management.tsx +0 -625
  157. package/templates/dynamics-365-starter/src/examples/entity-examples/opportunity-model.ts +0 -545
  158. package/templates/dynamics-365-starter/src/examples/integration-examples/custom-pcf-wrapper.tsx +0 -722
  159. package/templates/dynamics-365-starter/src/examples/workflow-examples/sales-workflow.ts +0 -662
  160. package/templates/dynamics-365-starter/src/page-templates/EntityDashboard.tsx +0 -519
  161. package/templates/dynamics-365-starter/src/page-templates/EntityDetailPage.tsx +0 -456
  162. package/templates/dynamics-365-starter/src/page-templates/EntityListPage.tsx +0 -406
  163. package/templates/dynamics-365-starter/src/page-templates/RelatedEntitiesPage.tsx +0 -578
  164. package/templates/dynamics-365-starter/src/page-templates/SearchPage.tsx +0 -629
  165. package/templates/dynamics-365-starter/tools/entity-generator/index.js +0 -168
  166. package/templates/dynamics-365-starter/tools/entity-generator/templates/constants.template.ts +0 -124
  167. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.css +0 -283
  168. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.tsx +0 -275
  169. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.css +0 -204
  170. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.tsx +0 -413
  171. package/templates/dynamics-365-starter/tools/entity-generator/templates/model.template.ts +0 -250
  172. package/templates/dynamics-365-starter/tools/metadata-sync/d365-client.js +0 -410
  173. package/templates/dynamics-365-starter/tools/metadata-sync/index.js +0 -512
  174. package/templates/dynamics-365-starter/tools/metadata-sync/type-generator.js +0 -675
  175. /package/templates/{dynamics-365-starter → react-custom-page}/README.md +0 -0
  176. /package/templates/{dynamics-365-starter → react-custom-page}/deployment/README.md +0 -0
  177. /package/templates/{dynamics-365-starter → react-custom-page}/docs/ARCHITECTURE_OVERVIEW.md +0 -0
  178. /package/templates/{dynamics-365-starter → react-custom-page}/docs/BEST_PRACTICES.md +0 -0
  179. /package/templates/{dynamics-365-starter → react-custom-page}/docs/MIGRATION_GUIDE.md +0 -0
  180. /package/templates/{dynamics-365-starter → react-custom-page}/public/index.html +0 -0
  181. /package/templates/{dynamics-365-starter → react-custom-page}/scripts/custom-build.js +0 -0
  182. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.css +0 -0
  183. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.tsx +0 -0
  184. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.css +0 -0
  185. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.tsx +0 -0
  186. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.css +0 -0
  187. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.tsx +0 -0
  188. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.css +0 -0
  189. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.tsx +0 -0
  190. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LogDialog.tsx +0 -0
  191. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingContext.tsx +0 -0
  192. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.css +0 -0
  193. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.tsx +0 -0
  194. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingProvider.tsx +0 -0
  195. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/logger.ts +0 -0
  196. /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/account.ts +0 -0
  197. /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/contact.ts +0 -0
  198. /package/templates/{dynamics-365-starter → react-custom-page}/src/index.tsx +0 -0
  199. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Account.ts +0 -0
  200. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/BaseEntity.ts +0 -0
  201. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Contact.ts +0 -0
  202. /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/ContactControlWrapper.tsx +0 -0
  203. /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/MultiEntityControlWrapper.tsx +0 -0
  204. /package/templates/{dynamics-365-starter → react-custom-page}/src/providers/DynamicsProvider.tsx +0 -0
  205. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/MockApiService.ts +0 -0
  206. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/ServiceFactory.ts +0 -0
  207. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/XrmApiService.ts +0 -0
  208. /package/templates/{dynamics-365-starter → react-custom-page}/src/styles/index.css +0 -0
  209. /package/templates/{dynamics-365-starter → react-custom-page}/tsconfig.json +0 -0
  210. /package/templates/{dynamics-365-starter → react-custom-page}/webpack.config.js +0 -0
@@ -0,0 +1,61 @@
1
+ import { IInputs, IOutputs } from "./generated/ManifestTypes";
2
+ import * as React from "react";
3
+ import * as ReactDOM from "react-dom";
4
+ import { {{componentName}}Component } from "./{{componentName}}Component";
5
+
6
+ export class {{componentName}} implements ComponentFramework.StandardControl<IInputs, IOutputs> {
7
+ private _value: {{tsType}};
8
+ private _container: HTMLDivElement;
9
+ private _context: ComponentFramework.Context<IInputs>;
10
+ private _notifyOutputChanged: () => void;
11
+
12
+ constructor() {}
13
+
14
+ public init(
15
+ context: ComponentFramework.Context<IInputs>,
16
+ notifyOutputChanged: () => void,
17
+ state: ComponentFramework.Dictionary,
18
+ container: HTMLDivElement
19
+ ): void {
20
+ this._context = context;
21
+ this._container = container;
22
+ this._notifyOutputChanged = notifyOutputChanged;
23
+ this._value = context.parameters.value.raw ?? {{valueDefault}};
24
+ }
25
+
26
+ public updateView(context: ComponentFramework.Context<IInputs>): void {
27
+ this._value = context.parameters.value.raw ?? {{valueDefault}};
28
+ this._context = context;
29
+
30
+ ReactDOM.render(
31
+ React.createElement({{componentName}}Component, {
32
+ value: this._value,
33
+ placeholder: context.parameters.placeholder.raw || "",
34
+ disabled: context.parameters.disabled.raw || false,
35
+ onChange: this.onValueChange.bind(this),
36
+ onBlur: this.onBlur.bind(this),
37
+ context: context
38
+ }),
39
+ this._container
40
+ );
41
+ }
42
+
43
+ private onValueChange = (newValue: {{tsType}}): void => {
44
+ this._value = newValue;
45
+ this._notifyOutputChanged();
46
+ };
47
+
48
+ private onBlur = (): void => {
49
+ this._notifyOutputChanged();
50
+ };
51
+
52
+ public getOutputs(): IOutputs {
53
+ return {
54
+ value: this._value
55
+ };
56
+ }
57
+
58
+ public destroy(): void {
59
+ ReactDOM.unmountComponentAtNode(this._container);
60
+ }
61
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "1.0.0",
4
+ "description": "{{componentName}} PCF control built with Dynamics UI Kit",
5
+ "scripts": {
6
+ "build": "pcf-scripts build",
7
+ "clean": "pcf-scripts clean",
8
+ "rebuild": "pcf-scripts rebuild",
9
+ "start": "pcf-scripts start",
10
+ "refreshTypes": "pcf-scripts refreshTypes"
11
+ },
12
+ "dependencies": {
13
+ "@fluentui/react": "^8.110.10",
14
+ "@types/powerapps-component-framework": "^1.3.4"
15
+ },
16
+ "devDependencies": {
17
+ "@microsoft/eslint-config-spfx": "^1.18.2",
18
+ "@types/node": "^18.16.9",
19
+ "@types/react": "^16.14.34",
20
+ "@types/react-dom": "^16.9.17",
21
+ "eslint-plugin-react-hooks": "^4.6.0",
22
+ "pcf-scripts": "^1.0.0",
23
+ "pcf-start": "^1.0.0",
24
+ "typescript": "^4.9.5"
25
+ },
26
+ "browserslist": [
27
+ "last 2 versions",
28
+ "ie 11"
29
+ ]
30
+ }
@@ -0,0 +1,47 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <root>
3
+ <resheader name="resmimetype">
4
+ <value>text/microsoft-resx</value>
5
+ </resheader>
6
+ <resheader name="version">
7
+ <value>2.0</value>
8
+ </resheader>
9
+ <resheader name="reader">
10
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
11
+ </resheader>
12
+ <resheader name="writer">
13
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
14
+ </resheader>
15
+ <data name="{{namespace}}_{{componentName}}" xml:space="preserve">
16
+ <value>{{componentName}}</value>
17
+ <comment>Name of the control</comment>
18
+ </data>
19
+ <data name="{{namespace}}_{{componentName}}_Desc" xml:space="preserve">
20
+ <value>{{componentName}} control built with Dynamics UI Kit</value>
21
+ <comment>Description of the control</comment>
22
+ </data>
23
+ <data name="Value" xml:space="preserve">
24
+ <value>Value</value>
25
+ <comment>Value property</comment>
26
+ </data>
27
+ <data name="Value_Desc" xml:space="preserve">
28
+ <value>The value of the control</value>
29
+ <comment>Description of the Value property</comment>
30
+ </data>
31
+ <data name="Placeholder" xml:space="preserve">
32
+ <value>Placeholder</value>
33
+ <comment>Placeholder property</comment>
34
+ </data>
35
+ <data name="Placeholder_Desc" xml:space="preserve">
36
+ <value>Placeholder text for the control</value>
37
+ <comment>Description of the Placeholder property</comment>
38
+ </data>
39
+ <data name="Disabled" xml:space="preserve">
40
+ <value>Disabled</value>
41
+ <comment>Disabled property</comment>
42
+ </data>
43
+ <data name="Disabled_Desc" xml:space="preserve">
44
+ <value>Whether the control is disabled</value>
45
+ <comment>Description of the Disabled property</comment>
46
+ </data>
47
+ </root>
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./node_modules/pcf-scripts/tsconfig_base.json",
3
+ "compilerOptions": {
4
+ "typeRoots": ["node_modules/@types"],
5
+ "jsx": "react",
6
+ "experimentalDecorators": true
7
+ }
8
+ }
@@ -0,0 +1,35 @@
1
+ import * as React from 'react';
2
+ import { initializeIcons } from '@fluentui/react/lib/Icons';
3
+ import { ValueInput } from './ValueInput';
4
+
5
+ // Initialize Fluent UI icons
6
+ initializeIcons();
7
+
8
+ export interface I{{componentName}}ComponentProps {
9
+ value: {{tsType}};
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ onChange: (value: {{tsType}}) => void;
13
+ onBlur: () => void;
14
+ context: ComponentFramework.Context<any>;
15
+ }
16
+
17
+ export const {{componentName}}Component: React.FC<I{{componentName}}ComponentProps> = ({
18
+ value,
19
+ placeholder,
20
+ disabled,
21
+ onChange,
22
+ onBlur,
23
+ }) => {
24
+ return (
25
+ <div style={{ width: '100%', height: '100%' }}>
26
+ <ValueInput
27
+ value={value}
28
+ placeholder={placeholder}
29
+ disabled={disabled}
30
+ onChange={onChange}
31
+ onBlur={onBlur}
32
+ />
33
+ </div>
34
+ );
35
+ };
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
5
+ .env.local
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
5
+ .env.local
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "dynamics-365-app",
2
+ "name": "react-custom-page-app",
3
3
  "version": "1.0.0",
4
- "description": "Dynamics 365 application built with Dynamics UI Kit",
4
+ "description": "Dynamics 365 React Custom Page app built with Dynamics UI Kit",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "build": "webpack --mode=production",
@@ -22,7 +22,6 @@
22
22
  "test:serve": "npm run build:prod && npm run serve",
23
23
  "precommit": "npm run quality",
24
24
  "prepublishOnly": "npm run validate",
25
- "generate:entity": "node tools/entity-generator/index.js generate",
26
25
  "metadata:pull": "node tools/metadata-sync/index.js pull",
27
26
  "metadata:generate": "node tools/metadata-sync/index.js generate",
28
27
  "metadata:validate": "node tools/metadata-sync/index.js validate",
@@ -35,6 +34,7 @@
35
34
  "react-dom": "^18.2.0"
36
35
  },
37
36
  "devDependencies": {
37
+ "@dataverse-kit/dataverse-codegen": "^0.1.0",
38
38
  "@types/react": "^18.2.0",
39
39
  "@types/react-dom": "^18.2.0",
40
40
  "commander": "^11.1.0",
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Metadata Sync — thin wrapper around @dataverse-kit/dataverse-codegen (dvgen).
5
+ *
6
+ * Pulls REAL entity metadata from a live Dataverse org and generates field
7
+ * constants + typed models (and, optionally, the IApiService CRUD layer) into
8
+ * `src/`. All org connection + auth is delegated to dvgen, which resolves, in
9
+ * order: --url/--token/--env-file flags, the DYNAMICS_* / VITE_DYNAMICS_* /
10
+ * REACT_APP_DYNAMICS_* env vars, then the Azure CLI (`az account get-access-token`).
11
+ *
12
+ * This replaces the previous mock pipeline (which returned hard-coded metadata).
13
+ * dvgen is the same tool the rest of the toolkit uses, so output stays consistent
14
+ * with `dvgen design`/`constants`/`models` everywhere.
15
+ */
16
+
17
+ const { program } = require('commander');
18
+ const { spawnSync } = require('child_process');
19
+ const path = require('path');
20
+ const fs = require('fs');
21
+
22
+ const DVGEN_CLI = path.join(
23
+ process.cwd(),
24
+ 'node_modules',
25
+ '@dataverse-kit',
26
+ 'dataverse-codegen',
27
+ 'dist',
28
+ 'cli.cjs'
29
+ );
30
+
31
+ function ensureDvgen() {
32
+ if (!fs.existsSync(DVGEN_CLI)) {
33
+ console.error(
34
+ '✗ dvgen not found. Install it first:\n' +
35
+ ' npm install -D @dataverse-kit/dataverse-codegen'
36
+ );
37
+ process.exit(1);
38
+ }
39
+ }
40
+
41
+ function dvgen(args) {
42
+ const result = spawnSync(process.execPath, [DVGEN_CLI, ...args], {
43
+ stdio: 'inherit',
44
+ });
45
+ return result.status === 0;
46
+ }
47
+
48
+ function resolveEntities(opts) {
49
+ if (opts.entities) {
50
+ return opts.entities
51
+ .split(',')
52
+ .map((e) => e.trim())
53
+ .filter(Boolean);
54
+ }
55
+ return ['account', 'contact'];
56
+ }
57
+
58
+ function connectionArgs(opts) {
59
+ const args = [];
60
+ if (opts.url) args.push('--url', opts.url);
61
+ if (opts.token) args.push('--token', opts.token);
62
+ if (opts.envFile) args.push('--env-file', opts.envFile);
63
+ return args;
64
+ }
65
+
66
+ function run(opts, { diff = false } = {}) {
67
+ ensureDvgen();
68
+ const entities = resolveEntities(opts);
69
+ const out = opts.out || 'src';
70
+ const conn = connectionArgs(opts);
71
+ const extra = diff ? ['--diff'] : [];
72
+ if (opts.force && !diff) extra.push('--force');
73
+ // The starter's shipped models import IApiService from
74
+ // @khester/dynamics-ui-api-client, so point generated models at the same
75
+ // module. When the dvgen-local CRUD layer is generated (--api), use that.
76
+ const apiServiceImport = opts.api ? '../services/IApiService' : opts.apiService;
77
+
78
+ console.log(
79
+ `${diff ? '🔍 Diffing' : '🔄 Generating'} ${entities.join(
80
+ ', '
81
+ )} via dvgen → ${out}/\n`
82
+ );
83
+
84
+ // Shared CRUD layer once (offline; only when explicitly requested).
85
+ if (opts.api && !diff) {
86
+ if (!dvgen(['api', '--out', out])) process.exit(1);
87
+ }
88
+
89
+ let ok = true;
90
+ for (const entity of entities) {
91
+ console.log(`\n→ ${entity}`);
92
+ if (
93
+ !dvgen(['constants', '--entity', entity, '--out', out, ...conn, ...extra])
94
+ ) {
95
+ ok = false;
96
+ }
97
+ const modelArgs = ['models', '--entity', entity, '--out', out, ...conn, ...extra];
98
+ if (apiServiceImport) modelArgs.push('--api-service', apiServiceImport);
99
+ if (opts.withRetrieve) modelArgs.push('--with-retrieve');
100
+ if (!dvgen(modelArgs)) ok = false;
101
+ }
102
+
103
+ if (!ok) {
104
+ console.error('\n✗ One or more entities failed.');
105
+ process.exit(1);
106
+ }
107
+ console.log('\n✓ Done.');
108
+ }
109
+
110
+ function withCommon(cmd) {
111
+ return cmd
112
+ .option(
113
+ '-e, --entities <list>',
114
+ 'comma-separated entities (default: account,contact)'
115
+ )
116
+ .option('--url <url>', 'Dataverse org URL (else dvgen resolves env / Azure CLI)')
117
+ .option('--token <token>', 'bearer token (else dvgen resolves env / Azure CLI)')
118
+ .option('--env-file <path>', '.env file with DYNAMICS_URL / DYNAMICS_TOKEN')
119
+ .option('-o, --out <dir>', 'output base dir', 'src')
120
+ .option('--force', 'overwrite existing generated files (default: skip)')
121
+ .option('--with-retrieve', 'include retrieveWithRelated() FetchXML scaffolds')
122
+ .option(
123
+ '--api-service <import>',
124
+ 'IApiService module specifier for generated models',
125
+ '@khester/dynamics-ui-api-client'
126
+ )
127
+ .option('--api', 'also (re)generate the IApiService CRUD layer');
128
+ }
129
+
130
+ program
131
+ .name('metadata-sync')
132
+ .description(
133
+ 'Live Dataverse metadata → TypeScript (constants + models) via dvgen'
134
+ );
135
+
136
+ withCommon(program.command('sync'))
137
+ .description('Pull live metadata and generate constants + models')
138
+ .action((o) => run(o));
139
+
140
+ withCommon(program.command('pull'))
141
+ .description('Alias of sync (pull live metadata + generate)')
142
+ .action((o) => run(o));
143
+
144
+ withCommon(program.command('generate'))
145
+ .description('Alias of sync (generate constants + models from the live org)')
146
+ .action((o) => run(o));
147
+
148
+ withCommon(program.command('validate'))
149
+ .description('Diff generated code against the live org (no writes)')
150
+ .action((o) => run(o, { diff: true }));
151
+
152
+ program.parse();
@@ -0,0 +1,36 @@
1
+ # {{projectName}}
2
+
3
+ An **Azure Static Web App** — React + Fluent UI ({{componentLibrary}}) frontend with a managed Azure
4
+ Functions `api/`.
5
+
6
+ ```
7
+ {{projectName}}/
8
+ ├── frontend/ # React + Vite + Fluent UI
9
+ ├── api/ # Azure Functions (Node 20, v4 model)
10
+ ├── staticwebapp.config.json
11
+ └── package.json # orchestrates + dev-tools
12
+ ```
13
+
14
+ ## Develop
15
+
16
+ ```bash
17
+ npm run install:all # install root + frontend + api
18
+ npm run dev # SWA CLI: frontend + Functions, proxied at /api
19
+ npm run build # build frontend + api
20
+ npm run typecheck # typecheck both
21
+ ```
22
+
23
+ `frontend/src/App.tsx` calls `/api/hello`. Add functions under `api/src/functions/`.
24
+
25
+ ## Dev-tools (Dataverse)
26
+
27
+ ```bash
28
+ npm run auth:token -- --url https://YOUR_ORG.crm.dynamics.com
29
+ npm run metadata:pull -- --entities account,contact
30
+ npm run smoke:live -- --entity account
31
+ ```
32
+
33
+ ## Deploy
34
+
35
+ Deploy with the SWA CLI or GitHub Actions (`Azure/static-web-apps-deploy`). App location `frontend`,
36
+ API location `api`, output `dist`.
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { ThemeProvider } from '@fluentui/react/lib/Theme';
3
+ import { Stack } from '@fluentui/react/lib/Stack';
4
+ import { Text } from '@fluentui/react/lib/Text';
5
+ import { PrimaryButton } from '@fluentui/react/lib/Button';
6
+
7
+ export const App: React.FC = () => {
8
+ const [message, setMessage] = React.useState('');
9
+
10
+ const callApi = async () => {
11
+ try {
12
+ const res = await fetch('/api/hello');
13
+ const data = (await res.json()) as { message?: string };
14
+ setMessage(data.message ?? '');
15
+ } catch (e) {
16
+ setMessage(`API call failed: ${String(e)}`);
17
+ }
18
+ };
19
+
20
+ return (
21
+ <ThemeProvider>
22
+ <Stack tokens={{ childrenGap: 12, padding: 16 }}>
23
+ <Text variant="xLarge">{{projectName}}</Text>
24
+ <Text>Azure Static Web App — Fluent UI v8 + Functions API.</Text>
25
+ <Stack.Item>
26
+ <PrimaryButton text="Call /api/hello" onClick={callApi} />
27
+ </Stack.Item>
28
+ {message && <Text>{message}</Text>}
29
+ </Stack>
30
+ </ThemeProvider>
31
+ );
32
+ };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { FluentProvider, webLightTheme, Text, Button } from '@fluentui/react-components';
3
+
4
+ export const App: React.FC = () => {
5
+ const [message, setMessage] = React.useState('');
6
+
7
+ const callApi = async () => {
8
+ try {
9
+ const res = await fetch('/api/hello');
10
+ const data = (await res.json()) as { message?: string };
11
+ setMessage(data.message ?? '');
12
+ } catch (e) {
13
+ setMessage(`API call failed: ${String(e)}`);
14
+ }
15
+ };
16
+
17
+ return (
18
+ <FluentProvider theme={webLightTheme}>
19
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 12, padding: 16 }}>
20
+ <Text size={600} weight="semibold">{{projectName}}</Text>
21
+ <Text>Azure Static Web App — Fluent UI v9 + Functions API.</Text>
22
+ <div>
23
+ <Button appearance="primary" onClick={callApi}>
24
+ Call /api/hello
25
+ </Button>
26
+ </div>
27
+ {message && <Text>{message}</Text>}
28
+ </div>
29
+ </FluentProvider>
30
+ );
31
+ };
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": "2.0",
3
+ "logging": {
4
+ "applicationInsights": {
5
+ "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" }
6
+ }
7
+ },
8
+ "extensionBundle": {
9
+ "id": "Microsoft.Azure.Functions.ExtensionBundle",
10
+ "version": "[4.*, 5.0.0)"
11
+ }
12
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{projectName}}-api",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "main": "dist/src/functions/*.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "watch": "tsc -w",
9
+ "start": "func start",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@azure/functions": "^4.5.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.11.0",
17
+ "typescript": "^5.3.3"
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
2
+
3
+ export async function hello(
4
+ request: HttpRequest,
5
+ context: InvocationContext
6
+ ): Promise<HttpResponseInit> {
7
+ context.log(`hello: ${request.method} ${request.url}`);
8
+ const name = request.query.get('name') ?? 'world';
9
+ return { jsonBody: { message: `Hello, ${name}! — from {{projectName}} API` } };
10
+ }
11
+
12
+ app.http('hello', {
13
+ methods: ['GET'],
14
+ authLevel: 'anonymous',
15
+ handler: hello,
16
+ });
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "moduleResolution": "node",
6
+ "outDir": "dist",
7
+ "rootDir": ".",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "sourceMap": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{projectName}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/index.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{projectName}}-frontend",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^18.2.0",
18
+ "@types/react-dom": "^18.2.0",
19
+ "@vitejs/plugin-react": "^4.2.1",
20
+ "typescript": "^5.3.3",
21
+ "vite": "^5.1.4"
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { App } from './App';
4
+
5
+ const container = document.getElementById('root');
6
+ if (container) {
7
+ createRoot(container).render(<App />);
8
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "bundler",
8
+ "jsx": "react-jsx",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "noEmit": true,
13
+ "isolatedModules": true
14
+ },
15
+ "include": ["src"]
16
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ build: { outDir: 'dist' },
7
+ server: {
8
+ proxy: {
9
+ // Local: proxy /api to the Functions host (swa start wires this in production).
10
+ '/api': 'http://localhost:7071',
11
+ },
12
+ },
13
+ });
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ dist/
3
+ frontend/dist/
4
+ api/dist/
5
+ .env
6
+ .env.local
7
+ local.settings.json
8
+ *.log
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "{{projectName}} — Azure Static Web App (React Fluent {{componentLibrary}} + Functions API)",
6
+ "scripts": {
7
+ "install:all": "npm install && npm --prefix frontend install && npm --prefix api install",
8
+ "dev": "swa start frontend --api-location api --run \"npm --prefix frontend run dev\"",
9
+ "build": "npm --prefix frontend run build && npm --prefix api run build",
10
+ "typecheck": "npm --prefix frontend run typecheck && npm --prefix api run typecheck"
11
+ },
12
+ "devDependencies": {
13
+ "@azure/static-web-apps-cli": "^1.1.10"
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "navigationFallback": {
3
+ "rewrite": "/index.html",
4
+ "exclude": ["/api/*", "/assets/*"]
5
+ },
6
+ "routes": [{ "route": "/api/*", "allowedRoles": ["anonymous"] }]
7
+ }