@khester/create-dynamics-app 3.1.0 → 3.2.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 (49) hide show
  1. package/README.md +2 -2
  2. package/dist/artifacts/registry.d.ts.map +1 -1
  3. package/dist/artifacts/registry.js +23 -5
  4. package/dist/artifacts/registry.js.map +1 -1
  5. package/dist/index.js +8 -0
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/templates/grid-customizer/README.md +105 -15
  9. package/templates/grid-customizer/dev/harness/MockGrid.tsx +165 -0
  10. package/templates/grid-customizer/dev/harness/dataverse.ts +168 -0
  11. package/templates/grid-customizer/dev/index.html +20 -0
  12. package/templates/grid-customizer/dev/main.tsx +260 -0
  13. package/templates/grid-customizer/dev/vite-env.d.ts +1 -0
  14. package/templates/grid-customizer/env.local.example +24 -0
  15. package/templates/grid-customizer/eslintrc.json +11 -0
  16. package/templates/grid-customizer/featureconfig.json +3 -0
  17. package/templates/grid-customizer/gitignore +3 -0
  18. package/templates/grid-customizer/package.json +30 -16
  19. package/templates/grid-customizer/pcfconfig.json +3 -0
  20. package/templates/grid-customizer/tsconfig.dev.json +16 -0
  21. package/templates/grid-customizer/tsconfig.json +17 -10
  22. package/templates/grid-customizer/vite.config.ts +64 -0
  23. package/templates/grid-customizer/{{componentName}}/ControlManifest.Input.xml +63 -0
  24. package/templates/grid-customizer/{{componentName}}/customizers/CellEditorOverrides.tsx +38 -0
  25. package/templates/grid-customizer/{{componentName}}/customizers/CellRendererOverrides.tsx +109 -0
  26. package/templates/grid-customizer/{{componentName}}/customizers/cells.tsx +137 -0
  27. package/templates/grid-customizer/{{componentName}}/customizers/config.ts +84 -0
  28. package/templates/grid-customizer/{{componentName}}/index.ts +81 -0
  29. package/templates/grid-customizer/{{componentName}}/strings/{{componentName}}.1033.resx +56 -0
  30. package/templates/grid-customizer/{{componentName}}/types.ts +168 -0
  31. package/templates/pcf-dataset/dev/main.tsx +5 -1
  32. package/templates/pcf-dataset/{{componentName}}/ControlManifest.Input.xml +8 -1
  33. package/templates/pcf-field/dev/harness/dataverse.ts +71 -0
  34. package/templates/pcf-field/dev/index.html +20 -0
  35. package/templates/pcf-field/dev/main.tsx +212 -0
  36. package/templates/pcf-field/dev/vite-env.d.ts +1 -0
  37. package/templates/pcf-field/env.local.example +24 -0
  38. package/templates/pcf-field/gitignore +4 -0
  39. package/templates/pcf-field/package.json +10 -2
  40. package/templates/pcf-field/tsconfig.dev.json +15 -0
  41. package/templates/pcf-field/tsconfig.json +20 -4
  42. package/templates/pcf-field/vite.config.ts +60 -0
  43. package/templates/pcf-field/{{componentName}}/{{componentName}}Component.tsx +6 -1
  44. package/templates/grid-customizer/src/GridCustomizer.ts +0 -28
  45. package/templates/grid-customizer/src/cell-renderers.tsx +0 -35
  46. package/templates/grid-customizer/src/index.ts +0 -4
  47. package/templates/grid-customizer/src/types/grid-types.ts +0 -30
  48. package/templates/grid-customizer/src/utils/color-utils.ts +0 -24
  49. package/templates/grid-customizer/webpack.config.js +0 -17
@@ -0,0 +1,60 @@
1
+ import { defineConfig, loadEnv } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import * as path from 'path';
4
+ import { dataverseProxy } from '@dataverse-kit/pcf-dev-harness/node';
5
+
6
+ /**
7
+ * Live-org dev harness for {{componentName}} — `npm run dev` → http://localhost:8182
8
+ *
9
+ * Complements `npm start` (the pcf-scripts harness on :8181) rather than replacing it: this
10
+ * control is control-type="standard" on React 16 and bundles its own React, so it loads fine
11
+ * in the stock harness. What the stock harness cannot do is show you REAL org data — it only
12
+ * takes hand-typed values. This one reads and writes an actual Dataverse column.
13
+ *
14
+ * Two data sources (see `dev/main.tsx`):
15
+ * - Mock — a local value. Default; needs no org.
16
+ * - Live — read a column off a real record through the same-origin `/api/data` proxy, edit
17
+ * it, and save it back. Enabled only when VITE_DYNAMICS_URL is set in `.env.local`.
18
+ *
19
+ * The proxy and its in-process Azure-CLI token refresh come from
20
+ * `@dataverse-kit/pcf-dev-harness/node`: the bearer token is injected SERVER-SIDE and never
21
+ * reaches the browser. `az login` + `npm run dev` is enough — `npm run auth:token` only writes
22
+ * an optional static fallback.
23
+ */
24
+ export default defineConfig(({ mode }) => {
25
+ // Read .env* from the PROJECT root (__dirname), not `root: 'dev'`. The empty prefix loads
26
+ // every var server-side; `envPrefix` below still limits what reaches the client bundle.
27
+ const env = loadEnv(mode, __dirname, '');
28
+ const live = dataverseProxy({
29
+ dynamicsUrl: env.VITE_DYNAMICS_URL || '',
30
+ tenant: env.DYNAMICS_TENANT,
31
+ // Array: `.env.local` is the documented file, `.env` is tolerated as a fallback so a token
32
+ // written by another tool still works. First file defining DYNAMICS_TOKEN wins.
33
+ envFile: [path.resolve(__dirname, '.env.local'), path.resolve(__dirname, '.env')],
34
+ pluginName: '{{componentName}}-live-token',
35
+ });
36
+
37
+ return {
38
+ root: 'dev',
39
+ // .env* live at the project root, not the `dev` root, so client code still sees
40
+ // import.meta.env.VITE_DYNAMICS_URL (which gates Live mode).
41
+ envDir: __dirname,
42
+ // SECURITY: the bearer token is acquired server-side by dataverseProxy and is never put in
43
+ // a VITE_-prefixed var, so Vite cannot inline it into the client bundle. envPrefix narrows
44
+ // client exposure to VITE_DYNAMICS_URL alone; the static fallback DYNAMICS_TOKEN is
45
+ // deliberately unprefixed for the same reason.
46
+ envPrefix: 'VITE_DYNAMICS_URL',
47
+ // jsxRuntime: 'classic' — the automatic runtime emits `react/jsx-runtime`, which does not
48
+ // exist in React 16. This control pins 16.14.0 (the floor the canvas runtime serves), and
49
+ // tsconfig sets `"jsx": "react"` (classic) for the same reason.
50
+ plugins: [react({ jsxRuntime: 'classic' }), live.plugin],
51
+ server: {
52
+ // 8182, because `pcf-scripts start` owns 8181 — both harnesses can run side by side.
53
+ // strictPort so a clash fails loudly instead of silently moving to another port.
54
+ port: 8182,
55
+ strictPort: true,
56
+ open: true,
57
+ proxy: live.proxy,
58
+ },
59
+ };
60
+ });
@@ -11,7 +11,12 @@ export interface I{{componentName}}ComponentProps {
11
11
  disabled?: boolean;
12
12
  onChange: (value: {{tsType}}) => void;
13
13
  onBlur: () => void;
14
- context: ComponentFramework.Context<any>;
14
+ /**
15
+ * The bound PCF context. OPTIONAL so the dev harness can render this component
16
+ * without a platform context (only the optionset variant reads it, for the
17
+ * bound column's choice metadata). The PCF host always supplies it.
18
+ */
19
+ context?: ComponentFramework.Context<any>;
15
20
  }
16
21
 
17
22
  export const {{componentName}}Component: React.FC<I{{componentName}}ComponentProps> = ({
@@ -1,28 +0,0 @@
1
- import type { IGridCustomizer, CellRendererOverrides } from './types/grid-types';
2
- import {
3
- StatusBadgeRenderer,
4
- CurrencyRenderer,
5
- DateRenderer,
6
- } from './cell-renderers';
7
-
8
- /**
9
- * {{componentName}} — editable-grid customizer. Map a column data type (or a
10
- * specific column logical name) to a cell renderer. Register this customizer on
11
- * the grid control's customizer property.
12
- */
13
- export class {{componentName}} implements IGridCustomizer {
14
- public cellRendererOverrides: CellRendererOverrides = {
15
- // By data type
16
- OptionSet: StatusBadgeRenderer,
17
- Currency: CurrencyRenderer,
18
- // By column logical name
19
- statuscode: StatusBadgeRenderer,
20
- statecode: StatusBadgeRenderer,
21
- createdon: DateRenderer,
22
- modifiedon: DateRenderer,
23
- };
24
-
25
- public cellEditorOverrides?: CellRendererOverrides = {
26
- // Add custom inline editors here, keyed the same way as renderers.
27
- };
28
- }
@@ -1,35 +0,0 @@
1
- import * as React from 'react';
2
- import { Badge, Text } from '@fluentui/react-components';
3
- import type { CellRenderer } from './types/grid-types';
4
- import { getStatusColor } from './utils/color-utils';
5
-
6
- /** Render an option-set / status value as a colored badge. */
7
- export const StatusBadgeRenderer: CellRenderer = ({ value, formattedValue }) => {
8
- const text = formattedValue ?? String(value ?? '');
9
- return (
10
- <Badge appearance="filled" style={{ backgroundColor: getStatusColor(text) }}>
11
- {text}
12
- </Badge>
13
- );
14
- };
15
-
16
- /** Render a number as currency. */
17
- export const CurrencyRenderer: CellRenderer = ({ value }) => {
18
- const n = typeof value === 'number' ? value : Number(value);
19
- const text = Number.isNaN(n)
20
- ? ''
21
- : new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD' }).format(n);
22
- return <Text>{text}</Text>;
23
- };
24
-
25
- /** Render a 0–100 number as a percentage. */
26
- export const ProgressRenderer: CellRenderer = ({ value }) => {
27
- const n = Math.max(0, Math.min(100, Number(value) || 0));
28
- return <Text>{n}%</Text>;
29
- };
30
-
31
- /** Render a date value as a localized date string. */
32
- export const DateRenderer: CellRenderer = ({ value }) => {
33
- const d = value instanceof Date ? value : new Date(String(value));
34
- return <Text>{Number.isNaN(d.getTime()) ? '' : d.toLocaleDateString()}</Text>;
35
- };
@@ -1,4 +0,0 @@
1
- export { {{componentName}} } from './GridCustomizer';
2
- export * from './cell-renderers';
3
- export * from './types/grid-types';
4
- export * from './utils/color-utils';
@@ -1,30 +0,0 @@
1
- import type * as React from 'react';
2
-
3
- export interface GridColumn {
4
- name: string;
5
- displayName: string;
6
- dataType: string;
7
- }
8
-
9
- export interface CellRendererProps {
10
- value: unknown;
11
- formattedValue?: string;
12
- column: GridColumn;
13
- rowData: Record<string, unknown>;
14
- entityId: string;
15
- entityName: string;
16
- isEditing: boolean;
17
- isDisabled: boolean;
18
- onChange?: (value: unknown) => void;
19
- }
20
-
21
- export type CellRenderer = React.FC<CellRendererProps>;
22
-
23
- export type CellRendererOverrides = {
24
- [key: string]: CellRenderer;
25
- };
26
-
27
- export interface IGridCustomizer {
28
- cellRendererOverrides: CellRendererOverrides;
29
- cellEditorOverrides?: CellRendererOverrides;
30
- }
@@ -1,24 +0,0 @@
1
- export function stringToColor(str: string): string {
2
- let hash = 0;
3
- for (let i = 0; i < str.length; i++) {
4
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
5
- }
6
- const hue = hash % 360;
7
- return `hsl(${hue}, 65%, 45%)`;
8
- }
9
-
10
- export const STATUS_COLORS: Record<string, string> = {
11
- active: '#107c10',
12
- inactive: '#a19f9d',
13
- draft: '#0078d4',
14
- pending: '#ffaa44',
15
- approved: '#107c10',
16
- rejected: '#d13438',
17
- completed: '#107c10',
18
- cancelled: '#a19f9d',
19
- };
20
-
21
- export function getStatusColor(status: string): string {
22
- const normalized = status.toLowerCase().replace(/\s+/g, '');
23
- return STATUS_COLORS[normalized] || stringToColor(status);
24
- }
@@ -1,17 +0,0 @@
1
- const path = require('path');
2
-
3
- module.exports = {
4
- entry: './src/index.ts',
5
- output: {
6
- path: path.resolve(__dirname, 'dist'),
7
- filename: '{{projectName}}.js',
8
- library: { type: 'umd', name: '{{componentName}}' },
9
- clean: true,
10
- },
11
- resolve: { extensions: ['.ts', '.tsx', '.js'] },
12
- module: {
13
- rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }],
14
- },
15
- // Provided by the grid host at runtime.
16
- externals: { react: 'React', 'react-dom': 'ReactDOM' },
17
- };