@khester/create-dynamics-app 1.0.4
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/bin/create-dynamics-app.js +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/copyTemplate.d.ts +2 -0
- package/dist/utils/copyTemplate.d.ts.map +1 -0
- package/dist/utils/copyTemplate.js +28 -0
- package/dist/utils/copyTemplate.js.map +1 -0
- package/dist/utils/initGit.d.ts +2 -0
- package/dist/utils/initGit.d.ts.map +1 -0
- package/dist/utils/initGit.js +122 -0
- package/dist/utils/initGit.js.map +1 -0
- package/dist/utils/installDependencies.d.ts +2 -0
- package/dist/utils/installDependencies.d.ts.map +1 -0
- package/dist/utils/installDependencies.js +40 -0
- package/dist/utils/installDependencies.js.map +1 -0
- package/dist/utils/updatePackageJson.d.ts +2 -0
- package/dist/utils/updatePackageJson.d.ts.map +1 -0
- package/dist/utils/updatePackageJson.js +24 -0
- package/dist/utils/updatePackageJson.js.map +1 -0
- package/package.json +51 -0
- package/templates/dynamics-365-starter/README.md +178 -0
- package/templates/dynamics-365-starter/package.json +44 -0
- package/templates/dynamics-365-starter/public/index.html +18 -0
- package/templates/dynamics-365-starter/src/components/ContactForm.css +48 -0
- package/templates/dynamics-365-starter/src/components/ContactForm.tsx +241 -0
- package/templates/dynamics-365-starter/src/components/ContactManagement.css +86 -0
- package/templates/dynamics-365-starter/src/components/ContactManagement.tsx +267 -0
- package/templates/dynamics-365-starter/src/index.tsx +40 -0
- package/templates/dynamics-365-starter/src/pcf/ContactControlWrapper.tsx +54 -0
- package/templates/dynamics-365-starter/src/providers/DynamicsProvider.tsx +136 -0
- package/templates/dynamics-365-starter/src/styles/index.css +104 -0
- package/templates/dynamics-365-starter/tsconfig.json +26 -0
- package/templates/dynamics-365-starter/webpack.config.js +58 -0
- package/templates/power-pages-starter/.env.example +6 -0
- package/templates/power-pages-starter/README.md +89 -0
- package/templates/power-pages-starter/package.json +42 -0
- package/templates/power-pages-starter/public/index.html +18 -0
- package/templates/power-pages-starter/src/components/ContactForm.css +84 -0
- package/templates/power-pages-starter/src/components/ContactForm.tsx +239 -0
- package/templates/power-pages-starter/src/index.tsx +32 -0
- package/templates/power-pages-starter/src/providers/PowerPagesProvider.tsx +139 -0
- package/templates/power-pages-starter/src/styles/index.css +76 -0
- package/templates/power-pages-starter/tsconfig.json +26 -0
- package/templates/power-pages-starter/webpack.config.js +52 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import { copyTemplate } from './utils/copyTemplate.js';
|
|
8
|
+
import { updatePackageJson } from './utils/updatePackageJson.js';
|
|
9
|
+
import { installDependencies } from './utils/installDependencies.js';
|
|
10
|
+
import { initGit } from './utils/initGit.js';
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name('create-dynamics-app')
|
|
14
|
+
.description('Create a new Dynamics UI Kit project')
|
|
15
|
+
.version('1.0.0')
|
|
16
|
+
.argument('<project-name>', 'name of the project')
|
|
17
|
+
.option('-t, --template <template>', 'template to use (power-pages, dynamics-365)')
|
|
18
|
+
.option('--skip-git', 'skip git repository initialization')
|
|
19
|
+
.option('--skip-install', 'skip dependency installation')
|
|
20
|
+
.action(async (projectName, options) => {
|
|
21
|
+
try {
|
|
22
|
+
console.log(chalk.cyan(`\nš Creating ${projectName}...\n`));
|
|
23
|
+
// Validate project name
|
|
24
|
+
if (!projectName || projectName.includes('/') || projectName.includes('\\')) {
|
|
25
|
+
console.error(chalk.red('ā Please provide a valid project name'));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
29
|
+
// Check if directory already exists
|
|
30
|
+
if (await fs.pathExists(projectPath)) {
|
|
31
|
+
console.error(chalk.red(`ā Directory ${projectName} already exists`));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
let template = options.template;
|
|
35
|
+
// If no template specified, prompt user
|
|
36
|
+
if (!template) {
|
|
37
|
+
const answers = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'list',
|
|
40
|
+
name: 'template',
|
|
41
|
+
message: 'Which template would you like to use?',
|
|
42
|
+
choices: [
|
|
43
|
+
{
|
|
44
|
+
name: 'š Power Pages - For Power Pages portal development',
|
|
45
|
+
value: 'power-pages',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'ā” Dynamics 365 - For Dynamics 365 custom controls and forms',
|
|
49
|
+
value: 'dynamics-365',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
template = answers.template;
|
|
55
|
+
}
|
|
56
|
+
// Validate template
|
|
57
|
+
if (!template || !['power-pages', 'dynamics-365'].includes(template)) {
|
|
58
|
+
console.error(chalk.red('ā Invalid template. Choose power-pages or dynamics-365'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Create project directory
|
|
62
|
+
await fs.ensureDir(projectPath);
|
|
63
|
+
// Copy template files
|
|
64
|
+
await copyTemplate(template, projectPath);
|
|
65
|
+
// Update package.json with project name
|
|
66
|
+
await updatePackageJson(projectPath, projectName);
|
|
67
|
+
// Initialize git repository
|
|
68
|
+
if (!options.skipGit) {
|
|
69
|
+
await initGit(projectPath);
|
|
70
|
+
}
|
|
71
|
+
// Install dependencies
|
|
72
|
+
if (!options.skipInstall) {
|
|
73
|
+
await installDependencies(projectPath);
|
|
74
|
+
}
|
|
75
|
+
// Success message
|
|
76
|
+
console.log(chalk.green(`\nā
Successfully created ${projectName}!\n`));
|
|
77
|
+
console.log(chalk.cyan('š Next steps:'));
|
|
78
|
+
console.log(` cd ${projectName}`);
|
|
79
|
+
if (options.skipInstall) {
|
|
80
|
+
console.log(' npm install');
|
|
81
|
+
}
|
|
82
|
+
if (template === 'power-pages') {
|
|
83
|
+
console.log(' npm run dev # Start development server');
|
|
84
|
+
console.log(' npm run build # Build for production');
|
|
85
|
+
console.log('\nš Don\'t forget to:');
|
|
86
|
+
console.log(' - Copy .env.example to .env');
|
|
87
|
+
console.log(' - Set your PORTAL_URL in .env');
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log(' npm run build # Build PCF components');
|
|
91
|
+
console.log(' npm run start # Start test harness');
|
|
92
|
+
console.log('\nš Check the README.md for deployment instructions');
|
|
93
|
+
}
|
|
94
|
+
console.log(chalk.magenta('\nš Happy coding!\n'));
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error(chalk.red('ā An error occurred:'), error);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
program.parse();
|
|
102
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyTemplate.d.ts","sourceRoot":"","sources":["../../src/utils/copyTemplate.ts"],"names":[],"mappings":"AASA,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB3F"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
export async function copyTemplate(template, destinationPath) {
|
|
9
|
+
const spinner = ora(`Copying ${template} template...`).start();
|
|
10
|
+
try {
|
|
11
|
+
const templatePath = path.join(__dirname, '../../templates', `${template}-starter`);
|
|
12
|
+
// Check if template exists
|
|
13
|
+
if (!(await fs.pathExists(templatePath))) {
|
|
14
|
+
throw new Error(`Template ${template} not found at ${templatePath}`);
|
|
15
|
+
}
|
|
16
|
+
// Copy all template files
|
|
17
|
+
await fs.copy(templatePath, destinationPath, {
|
|
18
|
+
overwrite: false,
|
|
19
|
+
errorOnExist: false,
|
|
20
|
+
});
|
|
21
|
+
spinner.succeed(chalk.green(`Template copied successfully`));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
spinner.fail(chalk.red('Failed to copy template'));
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=copyTemplate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyTemplate.js","sourceRoot":"","sources":["../../src/utils/copyTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,eAAuB;IAC1E,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,QAAQ,cAAc,CAAC,CAAC,KAAK,EAAE,CAAC;IAE/D,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG,QAAQ,UAAU,CAAC,CAAC;QAEpF,2BAA2B;QAC3B,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,iBAAiB,YAAY,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,0BAA0B;QAC1B,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,EAAE;YAC3C,SAAS,EAAE,KAAK;YAChB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initGit.d.ts","sourceRoot":"","sources":["../../src/utils/initGit.ts"],"names":[],"mappings":"AAMA,wBAAsB,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyHhE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
export async function initGit(projectPath) {
|
|
7
|
+
const spinner = ora('Initializing git repository...').start();
|
|
8
|
+
try {
|
|
9
|
+
// Create .gitignore if it doesn't exist
|
|
10
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
11
|
+
if (!(await fs.pathExists(gitignorePath))) {
|
|
12
|
+
const gitignoreContent = `# Dependencies
|
|
13
|
+
node_modules/
|
|
14
|
+
npm-debug.log*
|
|
15
|
+
yarn-debug.log*
|
|
16
|
+
yarn-error.log*
|
|
17
|
+
|
|
18
|
+
# Production builds
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
|
|
22
|
+
# Environment variables
|
|
23
|
+
.env
|
|
24
|
+
.env.local
|
|
25
|
+
.env.development.local
|
|
26
|
+
.env.test.local
|
|
27
|
+
.env.production.local
|
|
28
|
+
|
|
29
|
+
# IDE files
|
|
30
|
+
.vscode/
|
|
31
|
+
.idea/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
|
|
35
|
+
# OS generated files
|
|
36
|
+
.DS_Store
|
|
37
|
+
.DS_Store?
|
|
38
|
+
._*
|
|
39
|
+
.Spotlight-V100
|
|
40
|
+
.Trashes
|
|
41
|
+
ehthumbs.db
|
|
42
|
+
Thumbs.db
|
|
43
|
+
|
|
44
|
+
# Logs
|
|
45
|
+
logs
|
|
46
|
+
*.log
|
|
47
|
+
|
|
48
|
+
# Coverage directory used by tools like istanbul
|
|
49
|
+
coverage/
|
|
50
|
+
|
|
51
|
+
# Runtime data
|
|
52
|
+
pids
|
|
53
|
+
*.pid
|
|
54
|
+
*.seed
|
|
55
|
+
*.pid.lock
|
|
56
|
+
|
|
57
|
+
# Optional npm cache directory
|
|
58
|
+
.npm
|
|
59
|
+
|
|
60
|
+
# Optional eslint cache
|
|
61
|
+
.eslintcache
|
|
62
|
+
`;
|
|
63
|
+
await fs.writeFile(gitignorePath, gitignoreContent);
|
|
64
|
+
}
|
|
65
|
+
// Initialize git repository
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
const gitInit = spawn('git', ['init'], {
|
|
68
|
+
cwd: projectPath,
|
|
69
|
+
stdio: 'pipe',
|
|
70
|
+
});
|
|
71
|
+
gitInit.on('close', (code) => {
|
|
72
|
+
if (code === 0) {
|
|
73
|
+
resolve();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
reject(new Error(`git init failed with code ${code}`));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
gitInit.on('error', (error) => {
|
|
80
|
+
reject(error);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// Add initial commit
|
|
84
|
+
await new Promise((resolve, reject) => {
|
|
85
|
+
const gitAdd = spawn('git', ['add', '.'], {
|
|
86
|
+
cwd: projectPath,
|
|
87
|
+
stdio: 'pipe',
|
|
88
|
+
});
|
|
89
|
+
gitAdd.on('close', (code) => {
|
|
90
|
+
if (code === 0) {
|
|
91
|
+
const gitCommit = spawn('git', ['commit', '-m', 'Initial commit'], {
|
|
92
|
+
cwd: projectPath,
|
|
93
|
+
stdio: 'pipe',
|
|
94
|
+
});
|
|
95
|
+
gitCommit.on('close', (commitCode) => {
|
|
96
|
+
if (commitCode === 0) {
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
reject(new Error(`git commit failed with code ${commitCode}`));
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
gitCommit.on('error', (error) => {
|
|
104
|
+
reject(error);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
reject(new Error(`git add failed with code ${code}`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
gitAdd.on('error', (error) => {
|
|
112
|
+
reject(error);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
spinner.succeed(chalk.green('Git repository initialized'));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
spinner.warn(chalk.yellow('Git initialization skipped (git not available)'));
|
|
119
|
+
// Don't throw error for git issues, just warn
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=initGit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initGit.js","sourceRoot":"","sources":["../../src/utils/initGit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installDependencies.d.ts","sourceRoot":"","sources":["../../src/utils/installDependencies.ts"],"names":[],"mappings":"AAIA,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC5E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
export async function installDependencies(projectPath) {
|
|
5
|
+
const spinner = ora('Installing dependencies...').start();
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
let errorOutput = '';
|
|
8
|
+
const installProcess = spawn('npm', ['install'], {
|
|
9
|
+
cwd: projectPath,
|
|
10
|
+
stdio: 'pipe',
|
|
11
|
+
env: { ...process.env }
|
|
12
|
+
});
|
|
13
|
+
installProcess.stderr?.on('data', (data) => {
|
|
14
|
+
errorOutput += data.toString();
|
|
15
|
+
});
|
|
16
|
+
installProcess.on('close', (code) => {
|
|
17
|
+
if (code === 0) {
|
|
18
|
+
spinner.succeed(chalk.green('Dependencies installed successfully'));
|
|
19
|
+
resolve();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
spinner.fail(chalk.red('Failed to install dependencies'));
|
|
23
|
+
if (errorOutput.includes('E401') || errorOutput.includes('unauthenticated')) {
|
|
24
|
+
console.log(chalk.yellow('\nAuthentication required for private packages.'));
|
|
25
|
+
console.log(chalk.yellow('Please set up GitHub authentication:'));
|
|
26
|
+
console.log(chalk.cyan('1. Create a GitHub Personal Access Token with "read:packages" scope'));
|
|
27
|
+
console.log(chalk.cyan('2. Set the GITHUB_TOKEN environment variable:'));
|
|
28
|
+
console.log(chalk.cyan(' export GITHUB_TOKEN=your_token_here'));
|
|
29
|
+
console.log(chalk.cyan('3. Run npm install manually in the project directory'));
|
|
30
|
+
}
|
|
31
|
+
reject(new Error(`npm install failed with code ${code}`));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
installProcess.on('error', (error) => {
|
|
35
|
+
spinner.fail(chalk.red('Failed to install dependencies'));
|
|
36
|
+
reject(error);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=installDependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installDependencies.js","sourceRoot":"","sources":["../../src/utils/installDependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAAmB;IAC3D,MAAM,OAAO,GAAG,GAAG,CAAC,4BAA4B,CAAC,CAAC,KAAK,EAAE,CAAC;IAE1D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,WAAW,GAAG,EAAE,CAAC;QAErB,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE;YAC/C,GAAG,EAAE,WAAW;YAChB,KAAK,EAAE,MAAM;YACb,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;SACxB,CAAC,CAAC;QAEH,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzC,WAAW,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAClC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;gBACpE,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;gBAE1D,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;oBAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,iDAAiD,CAAC,CAAC,CAAC;oBAC7E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC,CAAC;oBAClE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC,CAAC;oBAC/F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC,CAAC;oBACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;oBAClE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC,CAAC;gBAClF,CAAC;gBAED,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACnC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updatePackageJson.d.ts","sourceRoot":"","sources":["../../src/utils/updatePackageJson.ts"],"names":[],"mappings":"AAKA,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB/F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
export async function updatePackageJson(projectPath, projectName) {
|
|
6
|
+
const spinner = ora('Updating package.json...').start();
|
|
7
|
+
try {
|
|
8
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
9
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
10
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
11
|
+
// Update the name
|
|
12
|
+
packageJson.name = projectName;
|
|
13
|
+
// Remove any template-specific fields that shouldn't be in final package
|
|
14
|
+
delete packageJson.template;
|
|
15
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
16
|
+
}
|
|
17
|
+
spinner.succeed(chalk.green('Package.json updated'));
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
spinner.fail(chalk.red('Failed to update package.json'));
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=updatePackageJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updatePackageJson.js","sourceRoot":"","sources":["../../src/utils/updatePackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,WAAmB,EAAE,WAAmB;IAC9E,MAAM,OAAO,GAAG,GAAG,CAAC,0BAA0B,CAAC,CAAC,KAAK,EAAE,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QAE/D,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACzC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YAEvD,kBAAkB;YAClB,WAAW,CAAC,IAAI,GAAG,WAAW,CAAC;YAE/B,yEAAyE;YACzE,OAAO,WAAW,CAAC,QAAQ,CAAC;YAE5B,MAAM,EAAE,CAAC,SAAS,CAAC,eAAe,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC,CAAC;QACzD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@khester/create-dynamics-app",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "CLI tool to scaffold new Dynamics UI Kit projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-dynamics-app": "./bin/create-dynamics-app.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"test": "jest --passWithNoTests",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
22
|
+
"clean": "rimraf dist"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"chalk": "^5.3.0",
|
|
26
|
+
"commander": "^11.1.0",
|
|
27
|
+
"fs-extra": "^11.2.0",
|
|
28
|
+
"inquirer": "^9.2.12",
|
|
29
|
+
"ora": "^7.0.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/fs-extra": "^11.0.4",
|
|
33
|
+
"@types/inquirer": "^9.0.7",
|
|
34
|
+
"rimraf": "^5.0.5",
|
|
35
|
+
"typescript": "^5.3.3"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"cli",
|
|
39
|
+
"scaffold",
|
|
40
|
+
"dynamics-365",
|
|
41
|
+
"power-pages",
|
|
42
|
+
"dynamics-ui-kit",
|
|
43
|
+
"create-app"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "restricted"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Dynamics 365 Application
|
|
2
|
+
|
|
3
|
+
This is a Dynamics 365 application built with Dynamics UI Kit components, featuring a complete contact management system.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Full CRUD Operations**: Create, read, update, and delete contacts
|
|
8
|
+
- **Advanced UI Components**: Uses DetailsList, Dialog, Panel, and Form components
|
|
9
|
+
- **PCF Integration**: Includes wrapper for PowerApps Component Framework
|
|
10
|
+
- **Responsive Design**: Works on desktop and mobile devices
|
|
11
|
+
- **Type Safety**: Full TypeScript support
|
|
12
|
+
|
|
13
|
+
## Getting Started
|
|
14
|
+
|
|
15
|
+
### Development
|
|
16
|
+
|
|
17
|
+
1. **Install Dependencies**
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. **Start Development Server**
|
|
23
|
+
```bash
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
This starts the development server at http://localhost:3000
|
|
27
|
+
|
|
28
|
+
3. **Build for Production**
|
|
29
|
+
```bash
|
|
30
|
+
npm run build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### PCF Integration
|
|
34
|
+
|
|
35
|
+
This project includes a PCF wrapper that allows you to use the contact management component as a custom control in Dynamics 365.
|
|
36
|
+
|
|
37
|
+
#### Setting up PCF
|
|
38
|
+
|
|
39
|
+
1. Install PCF CLI tools:
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g @microsoft/powerapps-cli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. Create a new PCF project:
|
|
45
|
+
```bash
|
|
46
|
+
pac pcf init --namespace YourNamespace --name ContactControl --template field
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. Copy the built components to your PCF project and integrate using `ContactControlWrapper`
|
|
50
|
+
|
|
51
|
+
## Project Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/
|
|
55
|
+
āāā components/ # React components
|
|
56
|
+
ā āāā ContactManagement.tsx # Main contact management interface
|
|
57
|
+
ā āāā ContactForm.tsx # Contact form component
|
|
58
|
+
ā āāā *.css # Component styles
|
|
59
|
+
āāā providers/ # Context providers
|
|
60
|
+
ā āāā DynamicsProvider.tsx # Dynamics 365 API provider
|
|
61
|
+
āāā pcf/ # PCF integration
|
|
62
|
+
ā āāā ContactControlWrapper.tsx # PCF wrapper component
|
|
63
|
+
āāā styles/ # Global styles
|
|
64
|
+
ā āāā index.css # Global CSS
|
|
65
|
+
āāā index.tsx # Application entry point
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Key Components
|
|
69
|
+
|
|
70
|
+
### ContactManagement
|
|
71
|
+
The main component that provides:
|
|
72
|
+
- Contact list with search and filtering
|
|
73
|
+
- CRUD operations (Create, Read, Update, Delete)
|
|
74
|
+
- Selection and bulk operations
|
|
75
|
+
- Responsive data grid
|
|
76
|
+
|
|
77
|
+
### ContactForm
|
|
78
|
+
A reusable form component for:
|
|
79
|
+
- Creating new contacts
|
|
80
|
+
- Editing existing contacts
|
|
81
|
+
- Form validation
|
|
82
|
+
- Error handling
|
|
83
|
+
|
|
84
|
+
### DynamicsProvider
|
|
85
|
+
Context provider that:
|
|
86
|
+
- Manages Dynamics 365 API connections
|
|
87
|
+
- Provides CRUD operations
|
|
88
|
+
- Handles authentication
|
|
89
|
+
- Error management
|
|
90
|
+
|
|
91
|
+
## Deployment Options
|
|
92
|
+
|
|
93
|
+
### Option 1: Web Resource
|
|
94
|
+
1. Build the application: `npm run build`
|
|
95
|
+
2. Upload `dist/main.js` and `dist/main.css` as web resources
|
|
96
|
+
3. Create an HTML web resource that includes these files
|
|
97
|
+
4. Add the HTML web resource to your Dynamics 365 forms or dashboards
|
|
98
|
+
|
|
99
|
+
### Option 2: PCF Custom Control
|
|
100
|
+
1. Use the `ContactControlWrapper` in your PCF project
|
|
101
|
+
2. Build and deploy as a PCF component
|
|
102
|
+
3. Add the custom control to forms, views, or dashboards
|
|
103
|
+
|
|
104
|
+
### Option 3: Canvas App
|
|
105
|
+
1. Build the application and host it externally
|
|
106
|
+
2. Embed in a Canvas App using an iframe
|
|
107
|
+
3. Use Power Platform connectors for data access
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
### Environment Variables
|
|
112
|
+
Create a `.env` file with:
|
|
113
|
+
```
|
|
114
|
+
DYNAMICS_BASE_URL=https://yourorg.crm.dynamics.com
|
|
115
|
+
DYNAMICS_ACCESS_TOKEN=your_access_token
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### API Configuration
|
|
119
|
+
The `DynamicsProvider` can be configured with:
|
|
120
|
+
- `baseUrl`: Your Dynamics 365 organization URL
|
|
121
|
+
- `accessToken`: Authentication token
|
|
122
|
+
- Custom API service implementations
|
|
123
|
+
|
|
124
|
+
## Customization
|
|
125
|
+
|
|
126
|
+
### Adding New Entities
|
|
127
|
+
1. Create new form components following the `ContactForm` pattern
|
|
128
|
+
2. Add CRUD operations to your provider
|
|
129
|
+
3. Create management components like `ContactManagement`
|
|
130
|
+
|
|
131
|
+
### Styling
|
|
132
|
+
- Global styles: `src/styles/index.css`
|
|
133
|
+
- Component styles: Co-located `.css` files
|
|
134
|
+
- Fluent UI theming: Customize in the provider
|
|
135
|
+
|
|
136
|
+
### API Integration
|
|
137
|
+
- Extend `DynamicsProvider` for additional entities
|
|
138
|
+
- Add custom business logic in service layers
|
|
139
|
+
- Implement caching and offline capabilities
|
|
140
|
+
|
|
141
|
+
## Available Scripts
|
|
142
|
+
|
|
143
|
+
- `npm run dev` - Start development server
|
|
144
|
+
- `npm run build` - Build for production
|
|
145
|
+
- `npm run start` - Alias for dev
|
|
146
|
+
- `npm run typecheck` - Type check without building
|
|
147
|
+
- `npm run clean` - Clean build directory
|
|
148
|
+
- `npm run lint` - Lint code
|
|
149
|
+
|
|
150
|
+
## Troubleshooting
|
|
151
|
+
|
|
152
|
+
### Common Issues
|
|
153
|
+
|
|
154
|
+
1. **Authentication Errors**
|
|
155
|
+
- Verify your access token and organization URL
|
|
156
|
+
- Check CORS settings in Dynamics 365
|
|
157
|
+
|
|
158
|
+
2. **Build Errors**
|
|
159
|
+
- Clear node_modules and reinstall
|
|
160
|
+
- Check TypeScript configuration
|
|
161
|
+
|
|
162
|
+
3. **PCF Integration**
|
|
163
|
+
- Ensure proper manifest configuration
|
|
164
|
+
- Check component lifecycle methods
|
|
165
|
+
|
|
166
|
+
## Learn More
|
|
167
|
+
|
|
168
|
+
- [Dynamics UI Kit Documentation](https://github.com/your-org/dynamics-ui-kit)
|
|
169
|
+
- [Dynamics 365 Web API](https://docs.microsoft.com/dynamics365/customer-engagement/web-api/)
|
|
170
|
+
- [PowerApps Component Framework](https://docs.microsoft.com/powerapps/developer/component-framework/)
|
|
171
|
+
- [Fluent UI React](https://developer.microsoft.com/fluentui)
|
|
172
|
+
|
|
173
|
+
## Support
|
|
174
|
+
|
|
175
|
+
For questions and support:
|
|
176
|
+
- Create an issue in the Dynamics UI Kit repository
|
|
177
|
+
- Check the documentation and examples
|
|
178
|
+
- Join the community discussions
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dynamics-365-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Dynamics 365 application built with Dynamics UI Kit",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "webpack --mode=production",
|
|
8
|
+
"dev": "webpack serve --mode=development",
|
|
9
|
+
"start": "webpack serve --mode=development",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"clean": "rimraf dist",
|
|
12
|
+
"lint": "eslint src --ext .ts,.tsx"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@khester1/dynamics-ui-components": "^1.0.0",
|
|
16
|
+
"@khester1/dynamics-ui-api-client": "^1.0.0",
|
|
17
|
+
"react": "^18.2.0",
|
|
18
|
+
"react-dom": "^18.2.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "^18.2.0",
|
|
22
|
+
"@types/react-dom": "^18.2.0",
|
|
23
|
+
"css-loader": "^6.8.1",
|
|
24
|
+
"html-webpack-plugin": "^5.5.3",
|
|
25
|
+
"style-loader": "^3.3.3",
|
|
26
|
+
"ts-loader": "^9.5.1",
|
|
27
|
+
"typescript": "^5.3.3",
|
|
28
|
+
"webpack": "^5.89.0",
|
|
29
|
+
"webpack-cli": "^5.1.4",
|
|
30
|
+
"webpack-dev-server": "^4.15.1"
|
|
31
|
+
},
|
|
32
|
+
"browserslist": {
|
|
33
|
+
"production": [
|
|
34
|
+
">0.2%",
|
|
35
|
+
"not dead",
|
|
36
|
+
"not op_mini all"
|
|
37
|
+
],
|
|
38
|
+
"development": [
|
|
39
|
+
"last 1 chrome version",
|
|
40
|
+
"last 1 firefox version",
|
|
41
|
+
"last 1 safari version"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|