@khester/create-dynamics-app 2.10.0 → 3.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 (82) hide show
  1. package/README.md +28 -3
  2. package/dist/artifacts/registry.d.ts +7 -5
  3. package/dist/artifacts/registry.d.ts.map +1 -1
  4. package/dist/artifacts/registry.js +82 -27
  5. package/dist/artifacts/registry.js.map +1 -1
  6. package/dist/artifacts/types.d.ts +15 -8
  7. package/dist/artifacts/types.d.ts.map +1 -1
  8. package/dist/index.js +7 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/scaffold.d.ts.map +1 -1
  11. package/dist/scaffold.js +9 -4
  12. package/dist/scaffold.js.map +1 -1
  13. package/dist/templatePlan.d.ts.map +1 -1
  14. package/dist/templatePlan.js +20 -3
  15. package/dist/templatePlan.js.map +1 -1
  16. package/dist/utils/copyTemplate.d.ts +1 -1
  17. package/dist/utils/copyTemplate.d.ts.map +1 -1
  18. package/dist/utils/copyTemplate.js +5 -1
  19. package/dist/utils/copyTemplate.js.map +1 -1
  20. package/package.json +1 -1
  21. package/templates/_shared/dataverse-seam/XrmApiService.ts +26 -5
  22. package/templates/host-swap/README.md +57 -2
  23. package/templates/host-swap/_variants/host.custom-page/src/App.tsx +9 -5
  24. package/templates/host-swap/_variants/host.pcf/ControlManifest.Input.xml +26 -2
  25. package/templates/host-swap/_variants/host.pcf/gitignore +7 -0
  26. package/templates/host-swap/_variants/host.pcf/index.ts +21 -3
  27. package/templates/host-swap/_variants/host.pcf/package.json +20 -4
  28. package/templates/host-swap/_variants/host.pcf/{{componentName}}.pcfproj +67 -0
  29. package/templates/host-swap/_variants/host.web-resource/src/App.tsx +5 -1
  30. package/templates/host-swap/src/Component.tsx +54 -7
  31. package/templates/host-swap/src/useHostWidth.ts +53 -0
  32. package/templates/pcf-dataset/dev/harness/dataverse.ts +82 -0
  33. package/templates/pcf-dataset/dev/index.html +20 -0
  34. package/templates/pcf-dataset/dev/main.tsx +190 -0
  35. package/templates/pcf-dataset/dev/vite-env.d.ts +1 -0
  36. package/templates/pcf-dataset/env.local.example +24 -0
  37. package/templates/pcf-dataset/eslintrc.json +11 -0
  38. package/templates/pcf-dataset/gitignore +4 -0
  39. package/templates/pcf-dataset/package.json +18 -8
  40. package/templates/pcf-dataset/pcfconfig.json +3 -0
  41. package/templates/pcf-dataset/tsconfig.dev.json +15 -0
  42. package/templates/pcf-dataset/tsconfig.json +11 -2
  43. package/templates/pcf-dataset/vite.config.ts +63 -0
  44. package/templates/pcf-dataset/{{componentName}}/ControlManifest.Input.xml +45 -0
  45. package/templates/pcf-dataset/{{componentName}}/{{componentName}}App.tsx +64 -0
  46. package/templates/pcf-dataset/{{{componentName}}Component.tsx → {{componentName}}/{{componentName}}Component.tsx} +9 -46
  47. package/templates/pcf-field/eslintrc.json +11 -0
  48. package/templates/pcf-field/package.json +1 -0
  49. package/templates/pcf-field/pcfconfig.json +3 -0
  50. package/templates/pcf-field/{{componentName}}/ControlManifest.Input.xml +42 -0
  51. package/templates/power-pages-island/README.md +93 -0
  52. package/templates/power-pages-island/package.json +27 -0
  53. package/templates/power-pages-island/public/index.html +13 -0
  54. package/templates/power-pages-island/src/App.tsx +37 -0
  55. package/templates/power-pages-island/src/Component.tsx +136 -0
  56. package/templates/power-pages-island/src/core/services/MockApiService.ts +58 -0
  57. package/templates/power-pages-island/src/core/services/portalSeam.assert.ts +14 -0
  58. package/templates/power-pages-island/src/island-env.d.ts +12 -0
  59. package/templates/power-pages-island/src/islands.tsx +38 -0
  60. package/templates/power-pages-island/src/useHostWidth.ts +53 -0
  61. package/templates/power-pages-island/tsconfig.json +15 -0
  62. package/templates/power-pages-island/webpack.config.js +22 -0
  63. package/templates/react-custom-page/tools/deploy/deploy-webresource.cjs +8 -2
  64. package/templates/pcf-dataset/ControlManifest.Input.xml +0 -16
  65. package/templates/pcf-field/ControlManifest.Input.xml +0 -17
  66. package/templates/power-pages-starter/.env.example +0 -6
  67. package/templates/power-pages-starter/README.md +0 -95
  68. package/templates/power-pages-starter/package.json +0 -43
  69. package/templates/power-pages-starter/public/index.html +0 -15
  70. package/templates/power-pages-starter/src/components/ContactForm.css +0 -84
  71. package/templates/power-pages-starter/src/components/ContactForm.tsx +0 -258
  72. package/templates/power-pages-starter/src/index.tsx +0 -32
  73. package/templates/power-pages-starter/src/providers/PowerPagesProvider.tsx +0 -162
  74. package/templates/power-pages-starter/src/styles/index.css +0 -76
  75. package/templates/power-pages-starter/tsconfig.json +0 -20
  76. package/templates/power-pages-starter/webpack.config.js +0 -57
  77. package/templates/pcf-dataset/{index.ts → {{componentName}}/index.ts} +0 -0
  78. package/templates/pcf-dataset/{strings → {{componentName}}/strings}/{{componentName}}.1033.resx +0 -0
  79. package/templates/pcf-field/{index.ts → {{componentName}}/index.ts} +0 -0
  80. package/templates/pcf-field/{strings → {{componentName}}/strings}/{{componentName}}.1033.resx +0 -0
  81. package/templates/pcf-field/{{{componentName}}Component.tsx → {{componentName}}/{{componentName}}Component.tsx} +0 -0
  82. package/templates/{power-pages-starter → power-pages-island}/gitignore +1 -1
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
3
+
4
+ // A Power Pages ISLAND: one self-contained bundle, uploaded as a portal WEB FILE and
5
+ // referenced from a Liquid page (see README). The output filename is STABLE (no
6
+ // contenthash) so the Liquid `<script src>` URL stays fixed across rebuilds.
7
+ // HtmlWebpackPlugin exists only for the local `webpack serve` preview — on a real
8
+ // portal the Liquid page is the host, so the emitted index.html is never uploaded.
9
+ module.exports = {
10
+ entry: './src/islands.tsx',
11
+ output: {
12
+ path: path.resolve(__dirname, 'dist'),
13
+ filename: '{{projectName}}.js',
14
+ clean: true,
15
+ },
16
+ resolve: { extensions: ['.ts', '.tsx', '.js'] },
17
+ module: {
18
+ rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }],
19
+ },
20
+ plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })],
21
+ devServer: { static: './dist', port: 8080, hot: true },
22
+ };
@@ -107,8 +107,14 @@ async function main() {
107
107
  await req("POST", "PublishXml", { ParameterXml: paramXml });
108
108
  console.log("✓ Published.");
109
109
 
110
- console.log(`\nOpen it: ${url}/WebResources/${name}`);
111
- console.log("Or host it on a model-driven custom page / open via Xrm.Navigation.navigateTo.");
110
+ // Surfacing guidance. NOTE: this app reads window.parent.Xrm / Xrm.Page — a model-driven FORM
111
+ // context. It therefore needs a host that provides one. It CANNOT run in a Power Apps custom page
112
+ // (that is a canvasapp; Xrm.Page does not exist there), and opening the raw /WebResources/ URL
113
+ // standalone gives it no parent Xrm either, so ServiceFactory throws.
114
+ console.log(`\nSurface it via a sitemap SubArea: $webresource:${name}`);
115
+ console.log("Or embed it on a model-driven form, or open it with Xrm.Navigation.navigateTo.");
116
+ console.log(`\nDirect URL (dev only — no parent Xrm, so production mode will fail):`);
117
+ console.log(` ${url}/WebResources/${name}`);
112
118
  }
113
119
 
114
120
  main().catch((e) => {
@@ -1,16 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8" ?>
2
- <manifest>
3
- <control namespace="{{namespace}}" constructor="{{componentName}}" version="1.0.0" display-name-key="{{namespace}}_{{componentName}}" description-key="{{namespace}}_{{componentName}}_Desc" control-type="virtual">
4
- <data-set name="dataset" display-name-key="Dataset_Display_Key">
5
- </data-set>
6
- <resources>
7
- <code path="index.ts" order="1"/>
8
- <platform-library name="React" version="18.2.0" />
9
- <platform-library name="Fluent" version="8.115.6" />
10
- <resx path="strings/{{componentName}}.1033.resx" version="1.0.0" />
11
- </resources>
12
- <feature-usage>
13
- <uses-feature name="Utility" required="true" />
14
- </feature-usage>
15
- </control>
16
- </manifest>
@@ -1,17 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8" ?>
2
- <manifest>
3
- <control namespace="{{namespace}}" constructor="{{componentName}}" version="1.0.0" display-name-key="{{namespace}}_{{componentName}}" description-key="{{namespace}}_{{componentName}}_Desc" control-type="standard">
4
- <property name="value" display-name-key="Value" description-key="Value_Desc" of-type="{{propertyType}}" usage="bound" required="true" />
5
- <property name="placeholder" display-name-key="Placeholder" description-key="Placeholder_Desc" of-type="SingleLine.Text" usage="input" required="false" />
6
- <property name="disabled" display-name-key="Disabled" description-key="Disabled_Desc" of-type="TwoOptions" usage="input" required="false" default-value="false" />
7
- <resources>
8
- <code path="index.ts" order="1"/>
9
- <platform-library name="React" version="16.8.6" />
10
- <platform-library name="Fluent" version="8.29.0" />
11
- <resx path="strings/{{componentName}}.1033.resx" version="1.0.0" />
12
- </resources>
13
- <feature-usage>
14
- <uses-feature name="Utility" required="true" />
15
- </feature-usage>
16
- </control>
17
- </manifest>
@@ -1,6 +0,0 @@
1
- # Power Pages Configuration
2
- PORTAL_URL=https://your-portal.powerappsportals.com
3
-
4
- # Optional: API Configuration
5
- API_VERSION=v9.0
6
- ENABLE_LOGGING=false
@@ -1,95 +0,0 @@
1
- # Power Pages Application
2
-
3
- This is a Power Pages application built with Dynamics UI Kit components.
4
-
5
- ## Getting Started
6
-
7
- 1. **Environment Setup**
8
-
9
- ```bash
10
- cp .env.example .env
11
- ```
12
-
13
- Edit `.env` and set your `PORTAL_URL` to your Power Pages portal URL.
14
-
15
- 2. **Install Dependencies**
16
-
17
- ```bash
18
- npm install
19
- ```
20
-
21
- 3. **Development**
22
-
23
- ```bash
24
- npm run dev
25
- ```
26
-
27
- This starts the development server at http://localhost:3000
28
-
29
- 4. **Build for Production**
30
- ```bash
31
- npm run build
32
- ```
33
-
34
- ## Project Structure
35
-
36
- ```
37
- src/
38
- ├── components/ # React components
39
- │ ├── ContactForm.tsx # Main contact form component
40
- │ └── ContactForm.css # Component styles
41
- ├── providers/ # Context providers
42
- │ └── PowerPagesProvider.tsx # Power Pages API provider
43
- ├── styles/ # Global styles
44
- │ └── index.css # Global CSS
45
- └── index.tsx # Application entry point
46
- ```
47
-
48
- ## Features
49
-
50
- - **Contact Management**: Create and manage contacts using Dynamics UI Kit components
51
- - **Power Pages Integration**: Seamless integration with Power Pages API
52
- - **Responsive Design**: Mobile-friendly responsive layout
53
- - **Type Safety**: Full TypeScript support
54
-
55
- ## Deployment
56
-
57
- To deploy your application to Power Pages:
58
-
59
- 1. Build the application:
60
-
61
- ```bash
62
- npm run build
63
- ```
64
-
65
- 2. Upload the `dist` folder contents to your Power Pages portal's web files
66
-
67
- 3. Create a new web page in Power Pages and reference your built JavaScript and CSS files
68
-
69
- ## Customization
70
-
71
- ### Adding New Components
72
-
73
- 1. Create your component in `src/components/`
74
- 2. Use Dynamics UI Kit components for consistent styling
75
- 3. Import and use the `usePowerPagesApi` hook for data operations
76
-
77
- ### Styling
78
-
79
- - Global styles are in `src/styles/index.css`
80
- - Component-specific styles should be co-located with components
81
- - Follow the existing CSS naming conventions
82
-
83
- ## Available Scripts
84
-
85
- - `npm run dev` - Start development server
86
- - `npm run build` - Build for production
87
- - `npm run typecheck` - Type check without building
88
- - `npm run clean` - Clean build directory
89
- - `npm run lint` - Lint code
90
-
91
- ## Learn More
92
-
93
- - [Dynamics UI Kit Documentation](https://github.com/your-org/dynamics-ui-kit)
94
- - [Power Pages Documentation](https://docs.microsoft.com/power-pages/)
95
- - [React Documentation](https://reactjs.org/)
@@ -1,43 +0,0 @@
1
- {
2
- "name": "power-pages-app",
3
- "version": "1.0.0",
4
- "description": "Power Pages 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
- "typecheck": "tsc --noEmit",
10
- "clean": "rimraf dist",
11
- "lint": "eslint src --ext .ts,.tsx"
12
- },
13
- "dependencies": {
14
- "@khester/dynamics-ui-api-client": "^1.0.0",
15
- "@khester/dynamics-ui-components": "^1.0.0",
16
- "react": "^18.2.0",
17
- "react-dom": "^18.2.0"
18
- },
19
- "devDependencies": {
20
- "@types/react": "^18.2.0",
21
- "@types/react-dom": "^18.2.0",
22
- "css-loader": "^6.8.1",
23
- "html-webpack-plugin": "^5.5.3",
24
- "style-loader": "^3.3.3",
25
- "ts-loader": "^9.5.1",
26
- "typescript": "^5.3.3",
27
- "webpack": "^5.89.0",
28
- "webpack-cli": "^5.1.4",
29
- "webpack-dev-server": "^4.15.1"
30
- },
31
- "browserslist": {
32
- "production": [
33
- ">0.2%",
34
- "not dead",
35
- "not op_mini all"
36
- ],
37
- "development": [
38
- "last 1 chrome version",
39
- "last 1 firefox version",
40
- "last 1 safari version"
41
- ]
42
- }
43
- }
@@ -1,15 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <link rel="icon" href="data:," />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <meta name="theme-color" content="#000000" />
8
- <meta name="description" content="Power Pages Application built with Dynamics UI Kit" />
9
- <title>Power Pages App - Dynamics UI Kit</title>
10
- </head>
11
- <body>
12
- <noscript>You need to enable JavaScript to run this app.</noscript>
13
- <div id="root"></div>
14
- </body>
15
- </html>
@@ -1,84 +0,0 @@
1
- .contact-form {
2
- max-width: 800px;
3
- margin: 0 auto;
4
- padding: 24px;
5
- background: #ffffff;
6
- border-radius: 8px;
7
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
8
- }
9
-
10
- .contact-form__header {
11
- margin-bottom: 24px;
12
- }
13
-
14
- .contact-form__title {
15
- margin: 0;
16
- font-size: 24px;
17
- font-weight: 600;
18
- color: #323130;
19
- }
20
-
21
- .contact-form__content {
22
- margin-bottom: 24px;
23
- }
24
-
25
- .contact-form__row {
26
- display: grid;
27
- grid-template-columns: 1fr 1fr;
28
- gap: 16px;
29
- margin-bottom: 16px;
30
- }
31
-
32
- .contact-form__field {
33
- display: flex;
34
- flex-direction: column;
35
- }
36
-
37
- .contact-form__actions {
38
- display: flex;
39
- gap: 12px;
40
- justify-content: flex-start;
41
- margin-top: 24px;
42
- }
43
-
44
- .contact-form__success {
45
- padding: 12px 16px;
46
- margin-bottom: 16px;
47
- background-color: #dff6dd;
48
- border: 1px solid #107c10;
49
- border-radius: 4px;
50
- color: #107c10;
51
- font-weight: 500;
52
- }
53
-
54
- .contact-form__error {
55
- padding: 12px 16px;
56
- margin-bottom: 16px;
57
- background-color: #fef7f1;
58
- border: 1px solid #d13438;
59
- border-radius: 4px;
60
- color: #d13438;
61
- font-weight: 500;
62
- }
63
-
64
- .contact-form__auth-notice {
65
- margin-top: 16px;
66
- padding: 12px 16px;
67
- background-color: #fff4ce;
68
- border: 1px solid #ffb900;
69
- border-radius: 4px;
70
- color: #8a6d00;
71
- text-align: center;
72
- }
73
-
74
- @media (max-width: 768px) {
75
- .contact-form {
76
- padding: 16px;
77
- margin: 16px;
78
- }
79
-
80
- .contact-form__row {
81
- grid-template-columns: 1fr;
82
- gap: 12px;
83
- }
84
- }
@@ -1,258 +0,0 @@
1
- import React, { useState, useCallback } from 'react';
2
- import {
3
- Button,
4
- TextField,
5
- Dropdown,
6
- DatePicker,
7
- } from '@khester/dynamics-ui-components';
8
- import { usePowerPagesApi } from '../providers/PowerPagesProvider';
9
- import './ContactForm.css';
10
-
11
- interface ContactFormData {
12
- firstname: string;
13
- lastname: string;
14
- emailaddress1: string;
15
- telephone1: string;
16
- preferredcontactmethodcode: number;
17
- birthdate: Date | null;
18
- }
19
-
20
- const preferredContactOptions = [
21
- { key: 'email', text: 'Email' },
22
- { key: 'phone', text: 'Phone' },
23
- { key: 'mail', text: 'Mail' },
24
- ];
25
-
26
- export const ContactForm: React.FC = () => {
27
- const { createRecord, isAuthenticated } = usePowerPagesApi();
28
-
29
- const [formData, setFormData] = useState<ContactFormData>({
30
- firstname: '',
31
- lastname: '',
32
- emailaddress1: '',
33
- telephone1: '',
34
- preferredcontactmethodcode: 1,
35
- birthdate: null,
36
- });
37
-
38
- const [errors, setErrors] = useState<Partial<ContactFormData>>({});
39
- const [isSubmitting, setIsSubmitting] = useState(false);
40
- const [submitError, setSubmitError] = useState<string>('');
41
- const [submitSuccess, setSubmitSuccess] = useState(false);
42
-
43
- const validateForm = useCallback((): boolean => {
44
- const newErrors: Partial<ContactFormData> = {};
45
-
46
- if (!formData.firstname.trim()) {
47
- newErrors.firstname = 'First name is required';
48
- }
49
-
50
- if (!formData.lastname.trim()) {
51
- newErrors.lastname = 'Last name is required';
52
- }
53
-
54
- if (!formData.emailaddress1.trim()) {
55
- newErrors.emailaddress1 = 'Email address is required';
56
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.emailaddress1)) {
57
- newErrors.emailaddress1 = 'Please enter a valid email address';
58
- }
59
-
60
- if (
61
- formData.telephone1 &&
62
- !/^\+?[\d\s\-\(\)]+$/.test(formData.telephone1)
63
- ) {
64
- newErrors.telephone1 = 'Please enter a valid phone number';
65
- }
66
-
67
- setErrors(newErrors);
68
- return Object.keys(newErrors).length === 0;
69
- }, [formData]);
70
-
71
- const handleInputChange = useCallback(
72
- (field: keyof ContactFormData, value: any) => {
73
- setFormData((prev) => ({ ...prev, [field]: value }));
74
-
75
- // Clear error for this field when user starts typing
76
- if (errors[field]) {
77
- setErrors((prev) => ({ ...prev, [field]: undefined }));
78
- }
79
- setSubmitError('');
80
- setSubmitSuccess(false);
81
- },
82
- [errors]
83
- );
84
-
85
- const handleSubmit = useCallback(async () => {
86
- if (!isAuthenticated) {
87
- setSubmitError('You must be signed in to save contact information');
88
- return;
89
- }
90
-
91
- if (!validateForm()) {
92
- return;
93
- }
94
-
95
- setIsSubmitting(true);
96
- setSubmitError('');
97
- setSubmitSuccess(false);
98
-
99
- try {
100
- const contactData = {
101
- firstname: formData.firstname.trim(),
102
- lastname: formData.lastname.trim(),
103
- emailaddress1: formData.emailaddress1.trim(),
104
- telephone1: formData.telephone1.trim() || null,
105
- preferredcontactmethodcode: formData.preferredcontactmethodcode,
106
- birthdate: formData.birthdate ? formData.birthdate.toISOString() : null,
107
- };
108
-
109
- await createRecord('contacts', contactData);
110
-
111
- setSubmitSuccess(true);
112
- // Reset form
113
- setFormData({
114
- firstname: '',
115
- lastname: '',
116
- emailaddress1: '',
117
- telephone1: '',
118
- preferredcontactmethodcode: 1,
119
- birthdate: null,
120
- });
121
- } catch (error) {
122
- setSubmitError(
123
- error instanceof Error
124
- ? error.message
125
- : 'An error occurred while saving'
126
- );
127
- } finally {
128
- setIsSubmitting(false);
129
- }
130
- }, [formData, isAuthenticated, validateForm, createRecord]);
131
-
132
- const handlePreferredContactChange = useCallback(
133
- (option: any) => {
134
- const code = option.key === 'email' ? 1 : option.key === 'phone' ? 2 : 3;
135
- handleInputChange('preferredcontactmethodcode', code);
136
- },
137
- [handleInputChange]
138
- );
139
-
140
- return (
141
- <div className="contact-form">
142
- <div className="contact-form__header">
143
- <h2 className="contact-form__title">Create New Contact</h2>
144
- </div>
145
-
146
- <div className="contact-form__content">
147
- {submitSuccess && (
148
- <div className="contact-form__success" role="alert">
149
- Contact created successfully!
150
- </div>
151
- )}
152
-
153
- {submitError && (
154
- <div className="contact-form__error" role="alert">
155
- {submitError}
156
- </div>
157
- )}
158
-
159
- <div className="contact-form__row">
160
- <div className="contact-form__field">
161
- <TextField
162
- label="First Name"
163
- required
164
- value={formData.firstname}
165
- onChange={(_, value) =>
166
- handleInputChange('firstname', value || '')
167
- }
168
- errorMessage={errors.firstname}
169
- disabled={isSubmitting}
170
- />
171
- </div>
172
- <div className="contact-form__field">
173
- <TextField
174
- label="Last Name"
175
- required
176
- value={formData.lastname}
177
- onChange={(_, value) =>
178
- handleInputChange('lastname', value || '')
179
- }
180
- errorMessage={errors.lastname}
181
- disabled={isSubmitting}
182
- />
183
- </div>
184
- </div>
185
-
186
- <div className="contact-form__row">
187
- <div className="contact-form__field">
188
- <TextField
189
- label="Email Address"
190
- type="email"
191
- required
192
- value={formData.emailaddress1}
193
- onChange={(_, value) =>
194
- handleInputChange('emailaddress1', value || '')
195
- }
196
- errorMessage={errors.emailaddress1}
197
- disabled={isSubmitting}
198
- />
199
- </div>
200
- <div className="contact-form__field">
201
- <TextField
202
- label="Phone Number"
203
- type="tel"
204
- value={formData.telephone1}
205
- onChange={(_, value) =>
206
- handleInputChange('telephone1', value || '')
207
- }
208
- errorMessage={errors.telephone1}
209
- disabled={isSubmitting}
210
- />
211
- </div>
212
- </div>
213
-
214
- <div className="contact-form__row">
215
- <div className="contact-form__field">
216
- <Dropdown
217
- label="Preferred Contact Method"
218
- options={preferredContactOptions}
219
- selectedKey={
220
- formData.preferredcontactmethodcode === 1
221
- ? 'email'
222
- : formData.preferredcontactmethodcode === 2
223
- ? 'phone'
224
- : 'mail'
225
- }
226
- onChange={handlePreferredContactChange}
227
- disabled={isSubmitting}
228
- />
229
- </div>
230
- <div className="contact-form__field">
231
- <DatePicker
232
- label="Birth Date"
233
- value={formData.birthdate || undefined}
234
- onSelectDate={(date) => handleInputChange('birthdate', date)}
235
- disabled={isSubmitting}
236
- placeholder="Select date..."
237
- />
238
- </div>
239
- </div>
240
- </div>
241
-
242
- <div className="contact-form__actions">
243
- <Button
244
- text="Save Contact"
245
- variant="primary"
246
- onClick={handleSubmit}
247
- disabled={isSubmitting || !isAuthenticated}
248
- />
249
- </div>
250
-
251
- {!isAuthenticated && (
252
- <div className="contact-form__auth-notice">
253
- Please sign in to save contact information.
254
- </div>
255
- )}
256
- </div>
257
- );
258
- };
@@ -1,32 +0,0 @@
1
- import React from 'react';
2
- import { createRoot } from 'react-dom/client';
3
- import { ContactForm } from './components/ContactForm';
4
- import { PowerPagesProvider } from './providers/PowerPagesProvider';
5
- import './styles/index.css';
6
-
7
- const App: React.FC = () => {
8
- return (
9
- <PowerPagesProvider>
10
- <div className="app">
11
- <header className="app-header">
12
- <h1>Power Pages Contact Management</h1>
13
- <p>Built with Dynamics UI Kit</p>
14
- </header>
15
-
16
- <main className="app-main">
17
- <ContactForm />
18
- </main>
19
-
20
- <footer className="app-footer">
21
- <p>&copy; 2024 Your Organization. Powered by Dynamics UI Kit.</p>
22
- </footer>
23
- </div>
24
- </PowerPagesProvider>
25
- );
26
- };
27
-
28
- const container = document.getElementById('root');
29
- if (!container) throw new Error('Failed to find the root element');
30
-
31
- const root = createRoot(container);
32
- root.render(<App />);