@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.
- package/README.md +74 -0
- package/dist/artifacts/registry.d.ts +18 -0
- package/dist/artifacts/registry.d.ts.map +1 -0
- package/dist/artifacts/registry.js +340 -0
- package/dist/artifacts/registry.js.map +1 -0
- package/dist/artifacts/types.d.ts +122 -0
- package/dist/artifacts/types.d.ts.map +1 -0
- package/dist/artifacts/types.js +7 -0
- package/dist/artifacts/types.js.map +1 -0
- package/dist/artifacts/validators.d.ts +16 -0
- package/dist/artifacts/validators.d.ts.map +1 -0
- package/dist/artifacts/validators.js +45 -0
- package/dist/artifacts/validators.js.map +1 -0
- package/dist/fromDesign.d.ts +5 -0
- package/dist/fromDesign.d.ts.map +1 -0
- package/dist/fromDesign.js +98 -0
- package/dist/fromDesign.js.map +1 -0
- package/dist/index.js +129 -177
- package/dist/index.js.map +1 -1
- package/dist/injectDevTools.d.ts +28 -0
- package/dist/injectDevTools.d.ts.map +1 -0
- package/dist/injectDevTools.js +148 -0
- package/dist/injectDevTools.js.map +1 -0
- package/dist/scaffold.d.ts +48 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +180 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/templatePlan.d.ts +3 -0
- package/dist/templatePlan.d.ts.map +1 -0
- package/dist/templatePlan.js +43 -0
- package/dist/templatePlan.js.map +1 -0
- package/dist/utils/copyTemplate.d.ts +13 -1
- package/dist/utils/copyTemplate.d.ts.map +1 -1
- package/dist/utils/copyTemplate.js +98 -4
- package/dist/utils/copyTemplate.js.map +1 -1
- package/dist/utils/updatePackageJson.d.ts +11 -1
- package/dist/utils/updatePackageJson.d.ts.map +1 -1
- package/dist/utils/updatePackageJson.js +12 -10
- package/dist/utils/updatePackageJson.js.map +1 -1
- package/package.json +10 -7
- package/templates/_shared/dev-tools/auth/get-token.js +72 -0
- package/templates/_shared/dev-tools/dev/mock-xrm.js +42 -0
- package/templates/_shared/dev-tools/metadata-sync/index.js +152 -0
- package/templates/_shared/dev-tools/smoke/test-retrieve.js +44 -0
- package/templates/dialog-form/README.md +27 -0
- package/templates/dialog-form/_variants/App.v8.tsx +39 -0
- package/templates/dialog-form/_variants/App.v9.tsx +41 -0
- package/templates/dialog-form/gitignore +5 -0
- package/templates/dialog-form/package.json +27 -0
- package/templates/dialog-form/public/index.html +11 -0
- package/templates/dialog-form/src/index.tsx +10 -0
- package/templates/dialog-form/src/services/dataverse.ts +30 -0
- package/templates/dialog-form/tsconfig.json +15 -0
- package/templates/dialog-form/webpack.config.js +17 -0
- package/templates/grid-customizer/README.md +28 -0
- package/templates/grid-customizer/gitignore +4 -0
- package/templates/grid-customizer/package.json +25 -0
- package/templates/grid-customizer/src/GridCustomizer.ts +28 -0
- package/templates/grid-customizer/src/cell-renderers.tsx +35 -0
- package/templates/grid-customizer/src/index.ts +4 -0
- package/templates/grid-customizer/src/types/grid-types.ts +30 -0
- package/templates/grid-customizer/src/utils/color-utils.ts +24 -0
- package/templates/grid-customizer/tsconfig.json +15 -0
- package/templates/grid-customizer/webpack.config.js +17 -0
- package/templates/pcf-dataset/ControlManifest.Input.xml +16 -0
- package/templates/pcf-dataset/README.md +21 -0
- package/templates/pcf-dataset/gitignore +5 -0
- package/templates/pcf-dataset/index.ts +39 -0
- package/templates/pcf-dataset/package.json +30 -0
- package/templates/pcf-dataset/strings/{{componentName}}.1033.resx +47 -0
- package/templates/pcf-dataset/tsconfig.json +8 -0
- package/templates/pcf-dataset/{{componentName}}Component.tsx +39 -0
- package/templates/pcf-field/ControlManifest.Input.xml +17 -0
- package/templates/pcf-field/README.md +95 -0
- package/templates/pcf-field/_variants/ValueInput.boolean.tsx +24 -0
- package/templates/pcf-field/_variants/ValueInput.date.tsx +27 -0
- package/templates/pcf-field/_variants/ValueInput.number.tsx +35 -0
- package/templates/pcf-field/_variants/ValueInput.text.tsx +27 -0
- package/templates/pcf-field/gitignore +5 -0
- package/templates/pcf-field/index.ts +61 -0
- package/templates/pcf-field/package.json +30 -0
- package/templates/pcf-field/strings/{{componentName}}.1033.resx +47 -0
- package/templates/pcf-field/tsconfig.json +8 -0
- package/templates/pcf-field/{{componentName}}Component.tsx +35 -0
- package/templates/power-pages-starter/gitignore +5 -0
- package/templates/react-custom-page/gitignore +5 -0
- package/templates/{dynamics-365-starter → react-custom-page}/package.json +3 -3
- package/templates/react-custom-page/tools/metadata-sync/index.js +152 -0
- package/templates/static-web-app/README.md +36 -0
- package/templates/static-web-app/_variants/App.v8.tsx +32 -0
- package/templates/static-web-app/_variants/App.v9.tsx +31 -0
- package/templates/static-web-app/api/host.json +12 -0
- package/templates/static-web-app/api/package.json +19 -0
- package/templates/static-web-app/api/src/functions/hello.ts +16 -0
- package/templates/static-web-app/api/tsconfig.json +14 -0
- package/templates/static-web-app/frontend/index.html +12 -0
- package/templates/static-web-app/frontend/package.json +23 -0
- package/templates/static-web-app/frontend/src/index.tsx +8 -0
- package/templates/static-web-app/frontend/tsconfig.json +16 -0
- package/templates/static-web-app/frontend/vite.config.ts +13 -0
- package/templates/static-web-app/gitignore +8 -0
- package/templates/static-web-app/package.json +15 -0
- package/templates/static-web-app/staticwebapp.config.json +7 -0
- package/templates/teams-app/README.md +27 -0
- package/templates/teams-app/_variants/graph.off.ts +7 -0
- package/templates/teams-app/_variants/graph.on.ts +22 -0
- package/templates/teams-app/appPackage/manifest.json +26 -0
- package/templates/teams-app/gitignore +5 -0
- package/templates/teams-app/index.html +12 -0
- package/templates/teams-app/package.json +26 -0
- package/templates/teams-app/src/App.tsx +25 -0
- package/templates/teams-app/src/index.tsx +8 -0
- package/templates/teams-app/tsconfig.json +16 -0
- package/templates/teams-app/vite.config.ts +9 -0
- package/templates/web-resource/README.md +39 -0
- package/templates/web-resource/_variants/App.v8.tsx +29 -0
- package/templates/web-resource/_variants/App.v9.tsx +28 -0
- package/templates/web-resource/gitignore +5 -0
- package/templates/web-resource/package.json +27 -0
- package/templates/web-resource/public/index.html +11 -0
- package/templates/web-resource/src/index.tsx +10 -0
- package/templates/web-resource/src/services/dataverse.ts +30 -0
- package/templates/web-resource/tsconfig.json +15 -0
- package/templates/web-resource/webpack.config.js +17 -0
- package/dist/utils/consultingHelpers.d.ts +0 -13
- package/dist/utils/consultingHelpers.d.ts.map +0 -1
- package/dist/utils/consultingHelpers.js +0 -569
- package/dist/utils/consultingHelpers.js.map +0 -1
- package/templates/dynamics-365-starter/INTEGRATION_TEST_RESULTS.md +0 -302
- package/templates/dynamics-365-starter/PHASE_4_COMPLETION_SUMMARY.md +0 -305
- package/templates/dynamics-365-starter/deployment/QUICKSTART-MAC.md +0 -507
- package/templates/dynamics-365-starter/deployment/QUICKSTART-WINDOWS.md +0 -372
- package/templates/dynamics-365-starter/deployment/pipelines/README.md +0 -375
- package/templates/dynamics-365-starter/deployment/pipelines/azure-pipelines.yml +0 -330
- package/templates/dynamics-365-starter/deployment/pipelines/github-actions.yml +0 -422
- package/templates/dynamics-365-starter/deployment/pipelines/jenkins.groovy +0 -636
- package/templates/dynamics-365-starter/deployment/scripts/deploy.ps1 +0 -417
- package/templates/dynamics-365-starter/deployment/scripts/deploy.sh +0 -582
- package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.ps1 +0 -486
- package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.sh +0 -567
- package/templates/dynamics-365-starter/deployment/scripts/validate-setup.ps1 +0 -703
- package/templates/dynamics-365-starter/deployment/scripts/validate-setup.sh +0 -671
- package/templates/dynamics-365-starter/docs/team-standards/README.md +0 -273
- package/templates/dynamics-365-starter/docs/team-standards/client-onboarding.md +0 -577
- package/templates/dynamics-365-starter/docs/team-standards/code-review-checklist.md +0 -359
- package/templates/dynamics-365-starter/docs/team-standards/coding-standards.md +0 -700
- package/templates/dynamics-365-starter/docs/team-standards/cross-platform-team-guide.md +0 -736
- package/templates/dynamics-365-starter/docs/team-standards/development-workflows.md +0 -727
- package/templates/dynamics-365-starter/docs/troubleshooting/common-errors.md +0 -758
- package/templates/dynamics-365-starter/docs/troubleshooting/platform-specific-issues.md +0 -878
- package/templates/dynamics-365-starter/src/client-project-template/README.md +0 -234
- package/templates/dynamics-365-starter/src/client-project-template/config/client.template.json +0 -114
- package/templates/dynamics-365-starter/src/client-project-template/config/environments/template.json +0 -186
- package/templates/dynamics-365-starter/src/client-project-template/scripts/client-setup.js +0 -667
- package/templates/dynamics-365-starter/src/examples/README.md +0 -52
- package/templates/dynamics-365-starter/src/examples/component-examples/opportunity-management.tsx +0 -625
- package/templates/dynamics-365-starter/src/examples/entity-examples/opportunity-model.ts +0 -545
- package/templates/dynamics-365-starter/src/examples/integration-examples/custom-pcf-wrapper.tsx +0 -722
- package/templates/dynamics-365-starter/src/examples/workflow-examples/sales-workflow.ts +0 -662
- package/templates/dynamics-365-starter/src/page-templates/EntityDashboard.tsx +0 -519
- package/templates/dynamics-365-starter/src/page-templates/EntityDetailPage.tsx +0 -456
- package/templates/dynamics-365-starter/src/page-templates/EntityListPage.tsx +0 -406
- package/templates/dynamics-365-starter/src/page-templates/RelatedEntitiesPage.tsx +0 -578
- package/templates/dynamics-365-starter/src/page-templates/SearchPage.tsx +0 -629
- package/templates/dynamics-365-starter/tools/entity-generator/index.js +0 -168
- package/templates/dynamics-365-starter/tools/entity-generator/templates/constants.template.ts +0 -124
- package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.css +0 -283
- package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.tsx +0 -275
- package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.css +0 -204
- package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.tsx +0 -413
- package/templates/dynamics-365-starter/tools/entity-generator/templates/model.template.ts +0 -250
- package/templates/dynamics-365-starter/tools/metadata-sync/d365-client.js +0 -410
- package/templates/dynamics-365-starter/tools/metadata-sync/index.js +0 -512
- package/templates/dynamics-365-starter/tools/metadata-sync/type-generator.js +0 -675
- /package/templates/{dynamics-365-starter → react-custom-page}/README.md +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/deployment/README.md +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/docs/ARCHITECTURE_OVERVIEW.md +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/docs/BEST_PRACTICES.md +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/docs/MIGRATION_GUIDE.md +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/public/index.html +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/scripts/custom-build.js +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LogDialog.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingContext.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingProvider.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/logger.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/account.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/contact.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/index.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Account.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/models/BaseEntity.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Contact.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/ContactControlWrapper.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/MultiEntityControlWrapper.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/providers/DynamicsProvider.tsx +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/services/MockApiService.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/services/ServiceFactory.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/services/XrmApiService.ts +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/src/styles/index.css +0 -0
- /package/templates/{dynamics-365-starter → react-custom-page}/tsconfig.json +0 -0
- /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,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
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "react-custom-page-app",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "Dynamics 365
|
|
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,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,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
|
+
}
|