create-ec-app 1.7.0 → 1.9.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 +72 -17
- package/dist/cssScope.js +3 -5
- package/dist/cssScope.js.map +1 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +129 -53
- package/dist/index.js.map +1 -1
- package/dist/libFunctions.d.ts +13 -6
- package/dist/libFunctions.d.ts.map +1 -1
- package/dist/libFunctions.js +24 -9
- package/dist/libFunctions.js.map +1 -1
- package/dist/pcf.d.ts.map +1 -1
- package/dist/pcf.js +4 -1
- package/dist/pcf.js.map +1 -1
- package/dist/portalContainers.js +7 -5
- package/dist/portalContainers.js.map +1 -1
- package/package.json +18 -11
- package/scripts/build-generated.mjs +59 -0
- package/scripts/refresh-shadcn-template.ts +406 -0
- package/scripts/smoke-scaffold.mjs +245 -0
- package/templates/base/eslint.config.js +1 -1
- package/templates/base/package-lock.json +380 -476
- package/templates/base/package.json +14 -19
- package/templates/pcf/base/package-lock.json +35 -53
- package/templates/targets/code-apps/AGENTS.md +187 -0
- package/templates/targets/code-apps/CLAUDE.md +1 -0
- package/templates/targets/code-apps/package.patch.json +1 -1
- package/templates/targets/power-pages/AGENTS.md +192 -0
- package/templates/targets/power-pages/CLAUDE.md +1 -0
- package/templates/targets/power-pages/README.md +22 -2
- package/templates/targets/power-pages/src/App.patch.tsx +3 -1
- package/templates/targets/power-pages/src/components/shared/AuthError.tsx +18 -0
- package/templates/targets/power-pages/src/context/AuthContext.tsx +0 -4
- package/templates/targets/swa/AGENTS.md +181 -0
- package/templates/targets/swa/CLAUDE.md +1 -0
- package/templates/targets/webresource/AGENTS.md +179 -67
- package/templates/targets/webresource/CLAUDE.md +1 -0
- package/templates/targets/webresource/README.md +5 -5
- package/templates/ui/kendo/package.patch.json +2 -2
- package/templates/ui/shadcn-ui/SHADCN_TEMPLATE.md +20 -0
- package/templates/ui/shadcn-ui/package.patch.json +18 -9
- package/templates/ui/shadcn-ui/src/components/ui/accordion.tsx +79 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert-dialog.tsx +199 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert.tsx +76 -0
- package/templates/ui/shadcn-ui/src/components/ui/aspect-ratio.tsx +11 -0
- package/templates/ui/shadcn-ui/src/components/ui/attachment.tsx +206 -0
- package/templates/ui/shadcn-ui/src/components/ui/avatar.tsx +110 -0
- package/templates/ui/shadcn-ui/src/components/ui/badge.tsx +49 -0
- package/templates/ui/shadcn-ui/src/components/ui/breadcrumb.tsx +122 -0
- package/templates/ui/shadcn-ui/src/components/ui/bubble.tsx +125 -0
- package/templates/ui/shadcn-ui/src/components/ui/button-group.tsx +83 -0
- package/templates/ui/shadcn-ui/src/components/ui/button.tsx +67 -0
- package/templates/ui/shadcn-ui/src/components/ui/calendar.tsx +222 -0
- package/templates/ui/shadcn-ui/src/components/ui/card.tsx +103 -0
- package/templates/ui/shadcn-ui/src/components/ui/carousel.tsx +240 -0
- package/templates/ui/shadcn-ui/src/components/ui/chart.tsx +373 -0
- package/templates/ui/shadcn-ui/src/components/ui/checkbox.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/collapsible.tsx +33 -0
- package/templates/ui/shadcn-ui/src/components/ui/combobox.tsx +299 -0
- package/templates/ui/shadcn-ui/src/components/ui/command.tsx +195 -0
- package/templates/ui/shadcn-ui/src/components/ui/context-menu.tsx +264 -0
- package/templates/ui/shadcn-ui/src/components/ui/dialog.tsx +170 -0
- package/templates/ui/shadcn-ui/src/components/ui/direction.tsx +22 -0
- package/templates/ui/shadcn-ui/src/components/ui/drawer.tsx +134 -0
- package/templates/ui/shadcn-ui/src/components/ui/dropdown-menu.tsx +272 -0
- package/templates/ui/shadcn-ui/src/components/ui/empty.tsx +104 -0
- package/templates/ui/shadcn-ui/src/components/ui/field.tsx +236 -0
- package/templates/ui/shadcn-ui/src/components/ui/hover-card.tsx +44 -0
- package/templates/ui/shadcn-ui/src/components/ui/input-group.tsx +156 -0
- package/templates/ui/shadcn-ui/src/components/ui/input-otp.tsx +87 -0
- package/templates/ui/shadcn-ui/src/components/ui/input.tsx +19 -0
- package/templates/ui/shadcn-ui/src/components/ui/item.tsx +196 -0
- package/templates/ui/shadcn-ui/src/components/ui/kbd.tsx +26 -0
- package/templates/ui/shadcn-ui/src/components/ui/label.tsx +22 -0
- package/templates/ui/shadcn-ui/src/components/ui/marker.tsx +69 -0
- package/templates/ui/shadcn-ui/src/components/ui/menubar.tsx +282 -0
- package/templates/ui/shadcn-ui/src/components/ui/message-scroller.tsx +129 -0
- package/templates/ui/shadcn-ui/src/components/ui/message.tsx +92 -0
- package/templates/ui/shadcn-ui/src/components/ui/native-select.tsx +61 -0
- package/templates/ui/shadcn-ui/src/components/ui/navigation-menu.tsx +164 -0
- package/templates/ui/shadcn-ui/src/components/ui/pagination.tsx +129 -0
- package/templates/ui/shadcn-ui/src/components/ui/popover.tsx +89 -0
- package/templates/ui/shadcn-ui/src/components/ui/progress.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/radio-group.tsx +42 -0
- package/templates/ui/shadcn-ui/src/components/ui/resizable.tsx +50 -0
- package/templates/ui/shadcn-ui/src/components/ui/scroll-area.tsx +53 -0
- package/templates/ui/shadcn-ui/src/components/ui/select.tsx +194 -0
- package/templates/ui/shadcn-ui/src/components/ui/separator.tsx +26 -0
- package/templates/ui/shadcn-ui/src/components/ui/sheet.tsx +149 -0
- package/templates/ui/shadcn-ui/src/components/ui/sidebar.tsx +702 -0
- package/templates/ui/shadcn-ui/src/components/ui/skeleton.tsx +13 -0
- package/templates/ui/shadcn-ui/src/components/ui/slider.tsx +59 -0
- package/templates/ui/shadcn-ui/src/components/ui/sonner.tsx +47 -0
- package/templates/ui/shadcn-ui/src/components/ui/spinner.tsx +10 -0
- package/templates/ui/shadcn-ui/src/components/ui/switch.tsx +33 -0
- package/templates/ui/shadcn-ui/src/components/ui/table.tsx +114 -0
- package/templates/ui/shadcn-ui/src/components/ui/tabs.tsx +90 -0
- package/templates/ui/shadcn-ui/src/components/ui/textarea.tsx +18 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle-group.tsx +87 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle.tsx +45 -0
- package/templates/ui/shadcn-ui/src/components/ui/tooltip.tsx +59 -0
- package/templates/ui/shadcn-ui/src/index.patch.css +0 -118
- package/templates/ui/shadcn-ui/src/runtime/PortalContainer.ts +8 -0
- package/templates/base/biome.json +0 -54
|
@@ -14,7 +14,7 @@ This is a Power Pages Single Page Application (SPA) built with React, Vite, and
|
|
|
14
14
|
|
|
15
15
|
## Auth and API Access
|
|
16
16
|
|
|
17
|
-
This SPA uses ADAL (Azure Active Directory Authentication Library) for authentication.
|
|
17
|
+
This SPA uses ADAL (Azure Active Directory Authentication Library) for authentication. ADAL is deprecated by Microsoft and should not be treated as a long-term ideal path, but this template keeps the existing ADAL implementation for Power Pages scenarios until a validated MSAL v2 migration is introduced.
|
|
18
18
|
|
|
19
19
|
### Authentication Setup
|
|
20
20
|
|
|
@@ -37,6 +37,26 @@ The `AuthContext` requires the following environment variables:
|
|
|
37
37
|
- After successful authentication, users are redirected back with tokens
|
|
38
38
|
- Tokens are automatically cached and reused for subsequent requests
|
|
39
39
|
- Call `logout()` to clear tokens and sign out the user
|
|
40
|
+
- Auth errors are exposed as `error` on `useAuth()` and can be cleared with `clearError()`
|
|
41
|
+
|
|
42
|
+
### Authentication Errors
|
|
43
|
+
|
|
44
|
+
The generated app includes `src/components/shared/AuthError.tsx` and renders it near the app root. Keep that pattern if you replace the root layout:
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { AuthError } from "@/components/shared/AuthError";
|
|
48
|
+
|
|
49
|
+
function App() {
|
|
50
|
+
return (
|
|
51
|
+
<main>
|
|
52
|
+
<AuthError />
|
|
53
|
+
{/* app routes or page content */}
|
|
54
|
+
</main>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`AuthContext` does not throw automatically when authentication fails, so the app can display a local error and let the user dismiss it.
|
|
40
60
|
|
|
41
61
|
## Example Data Fetch Using TanStack Query and AuthContext
|
|
42
62
|
|
|
@@ -156,7 +176,7 @@ export const AuthButton = () => {
|
|
|
156
176
|
<Button>Click me</Button>;
|
|
157
177
|
```
|
|
158
178
|
|
|
159
|
-
- Shadcn/ui: Components are
|
|
179
|
+
- Shadcn/ui: Components are copied from the committed template snapshot and available under the `@/components` alias. Example:
|
|
160
180
|
|
|
161
181
|
```tsx
|
|
162
182
|
import { Button } from "@/components/ui/button";
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useAuth } from "./context/AuthContext";
|
|
2
|
+
import { AuthError } from "./components/shared/AuthError";
|
|
2
3
|
|
|
3
4
|
function App() {
|
|
4
|
-
const { isAuthenticated
|
|
5
|
+
const { isAuthenticated } = useAuth();
|
|
5
6
|
|
|
6
7
|
return (
|
|
7
8
|
<div className="flex h-screen flex-col items-center justify-center gap-4">
|
|
9
|
+
<AuthError />
|
|
8
10
|
{isAuthenticated ? (
|
|
9
11
|
<div>You are logged in</div>
|
|
10
12
|
) : (
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useAuth } from "@/context/AuthContext";
|
|
2
|
+
|
|
3
|
+
export function AuthError() {
|
|
4
|
+
const { error, clearError } = useAuth();
|
|
5
|
+
|
|
6
|
+
if (!error) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div role="alert" className="rounded border p-3">
|
|
12
|
+
<p>Authentication error: {error}</p>
|
|
13
|
+
<button type="button" onClick={clearError}>
|
|
14
|
+
Dismiss
|
|
15
|
+
</button>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -263,10 +263,6 @@ const AuthProviderContent = ({ children }: { children: ReactNode }) => {
|
|
|
263
263
|
getIdToken,
|
|
264
264
|
};
|
|
265
265
|
|
|
266
|
-
if (error) {
|
|
267
|
-
throw new Error(`Authentication error: ${error}`);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
266
|
return (
|
|
271
267
|
<AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
|
272
268
|
);
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
## Purpose
|
|
2
|
+
|
|
3
|
+
This repository is an Azure Static Web Apps frontend using React, TypeScript, and Vite.
|
|
4
|
+
|
|
5
|
+
Treat it as a static Azure-hosted SPA. Keep it small, readable, and easy to deploy.
|
|
6
|
+
|
|
7
|
+
Use the global AGENTS.md rules first. This file adds project-specific constraints.
|
|
8
|
+
|
|
9
|
+
## Hard Constraints
|
|
10
|
+
|
|
11
|
+
- Keep Static Web Apps hosting working.
|
|
12
|
+
- Keep local Vite development working.
|
|
13
|
+
- Keep SPA routing fallback working.
|
|
14
|
+
- Keep the app client-side unless an Azure Functions API already exists or is explicitly requested.
|
|
15
|
+
- Make surgical changes.
|
|
16
|
+
|
|
17
|
+
Do not add Dynamics `Xrm`, `token.json`, Power Pages ADAL, or Power Apps code app SDK patterns unless the target changes.
|
|
18
|
+
|
|
19
|
+
## Runtime Modes
|
|
20
|
+
|
|
21
|
+
The app supports two modes.
|
|
22
|
+
|
|
23
|
+
### Static Web Apps-hosted
|
|
24
|
+
|
|
25
|
+
- Build output is served from `dist`.
|
|
26
|
+
- Preserve `staticwebapp.config.json`.
|
|
27
|
+
- Keep `navigationFallback` when the app uses client-side routing.
|
|
28
|
+
- Use `staticwebapp.config.json` for SWA routes, auth, headers, response overrides, and fallback rules.
|
|
29
|
+
- Do not add deprecated `routes.json`.
|
|
30
|
+
- Do not rely on client-only route guards for sensitive data. Backend APIs must enforce auth/roles.
|
|
31
|
+
|
|
32
|
+
### Local dev
|
|
33
|
+
|
|
34
|
+
- Use Vite for normal UI development.
|
|
35
|
+
- Use the Static Web Apps CLI only when testing SWA routing, auth, or `/api` integration locally.
|
|
36
|
+
- Keep `swa-cli.config.json` aligned with the Vite dev server and `dist` output.
|
|
37
|
+
- Do not require a deployed Azure Static Web App for ordinary component work.
|
|
38
|
+
|
|
39
|
+
## Critical Files
|
|
40
|
+
|
|
41
|
+
| File | Rule |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `staticwebapp.config.json` | Static Web Apps routing/auth/fallback boundary. Preserve SPA fallback unless routing is removed. |
|
|
44
|
+
| `swa-cli.config.json` | Local SWA CLI configuration. Keep `appDevserverUrl`, build command, and output location accurate. |
|
|
45
|
+
| `vite.config.ts` | Preserve React, Tailwind, alias, and any existing build assumptions. |
|
|
46
|
+
| `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
|
|
47
|
+
| `package.json` | Keep SWA CLI scripts/dependencies only if the project uses them. |
|
|
48
|
+
| `api/` | Only add or change when the app actually has a Static Web Apps API requirement. |
|
|
49
|
+
|
|
50
|
+
## API and Data Access
|
|
51
|
+
|
|
52
|
+
Prefer direct, boring calls to the app's own API or public endpoints.
|
|
53
|
+
|
|
54
|
+
Use:
|
|
55
|
+
|
|
56
|
+
- relative `/api/...` calls for Static Web Apps managed APIs
|
|
57
|
+
- explicit response types near the call site
|
|
58
|
+
- narrow payloads
|
|
59
|
+
- direct `fetch` inside service files
|
|
60
|
+
- clear `response.ok` checks with useful status text
|
|
61
|
+
- `import.meta.env.VITE_*` only for public browser-safe values
|
|
62
|
+
|
|
63
|
+
Avoid:
|
|
64
|
+
|
|
65
|
+
- putting secrets in client-side environment variables
|
|
66
|
+
- direct Dataverse browser calls copied from webresource projects
|
|
67
|
+
- `window.Xrm`
|
|
68
|
+
- Power Pages `_api` assumptions
|
|
69
|
+
- Power Apps generated service assumptions
|
|
70
|
+
- generic API clients for one or two endpoints
|
|
71
|
+
- silent fallbacks for failed required calls
|
|
72
|
+
|
|
73
|
+
If the app needs private data, put the enforcement in the SWA API or configured route auth. Client-side hiding is not security.
|
|
74
|
+
|
|
75
|
+
## Validation
|
|
76
|
+
|
|
77
|
+
Use TypeScript types for trusted internal data and vendor-shaped responses.
|
|
78
|
+
|
|
79
|
+
Use runtime validation only when the current feature needs it, such as:
|
|
80
|
+
|
|
81
|
+
- user-entered form data
|
|
82
|
+
- URL/search parameters that control behavior
|
|
83
|
+
- local config that can be wrong
|
|
84
|
+
- data that crosses into an API write
|
|
85
|
+
- security-sensitive or data-loss-prone paths
|
|
86
|
+
|
|
87
|
+
Do not validate, normalize, or reformat values just because it is possible.
|
|
88
|
+
|
|
89
|
+
## Services, Queries, and Mutations
|
|
90
|
+
|
|
91
|
+
Keep service files explicit.
|
|
92
|
+
|
|
93
|
+
Preferred shape:
|
|
94
|
+
|
|
95
|
+
- one fetch/save function for the operation
|
|
96
|
+
- one TanStack Query hook when components need it
|
|
97
|
+
- one mutation hook when mutation state or invalidation is needed
|
|
98
|
+
- query keys colocated with the hook when reused for invalidation
|
|
99
|
+
|
|
100
|
+
Do not create wrapper chains such as:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
resolveConfig -> normalizeInput -> validateInput -> buildRequest -> executeRequest
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Prefer direct flow:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
read env/config -> fetch -> check response -> return typed data
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## State Management
|
|
113
|
+
|
|
114
|
+
- Use TanStack Query for server state.
|
|
115
|
+
- Use local component state for local UI behavior.
|
|
116
|
+
- Use Zustand only for shared client state that has outgrown local state.
|
|
117
|
+
- Do not store server state in Zustand.
|
|
118
|
+
- Do not add Redux unless explicitly requested.
|
|
119
|
+
|
|
120
|
+
## UI and Styling
|
|
121
|
+
|
|
122
|
+
Stay consistent with the project's existing UI system.
|
|
123
|
+
|
|
124
|
+
- Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
|
|
125
|
+
- Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
|
|
126
|
+
- Preserve the existing theme and global CSS imports.
|
|
127
|
+
- Do not mix UI systems unless explicitly asked.
|
|
128
|
+
- Do not hand-roll custom CSS unless component props and Tailwind are not enough.
|
|
129
|
+
- Keep layouts compact, responsive, and suitable for a hosted business app.
|
|
130
|
+
|
|
131
|
+
## Code Shape
|
|
132
|
+
|
|
133
|
+
Prefer:
|
|
134
|
+
|
|
135
|
+
- focused React components
|
|
136
|
+
- direct typed functions
|
|
137
|
+
- existing services and components
|
|
138
|
+
- small local helpers only when they remove real duplication or name non-obvious domain logic
|
|
139
|
+
|
|
140
|
+
Avoid:
|
|
141
|
+
|
|
142
|
+
- broad factories
|
|
143
|
+
- generic service clients
|
|
144
|
+
- classes for simple service logic
|
|
145
|
+
- excessive configuration
|
|
146
|
+
- defensive wrappers around every value
|
|
147
|
+
- broad refactors while adding a feature
|
|
148
|
+
|
|
149
|
+
## Error Handling
|
|
150
|
+
|
|
151
|
+
Required reads, saves, deletes, uploads, downloads, auth failures, and required parsing failures should throw.
|
|
152
|
+
|
|
153
|
+
Do not swallow failed fetches and treat them as empty data unless the requirement explicitly says the feature is best-effort.
|
|
154
|
+
|
|
155
|
+
Include response status and useful response text where practical.
|
|
156
|
+
|
|
157
|
+
## Build and Deployment
|
|
158
|
+
|
|
159
|
+
Do not replace Vite, add SSR, add Next.js, or introduce backend coupling unless explicitly asked.
|
|
160
|
+
|
|
161
|
+
Keep `staticwebapp.config.json` deployable at the output root. If the build flow changes, verify the SWA config still reaches `dist`.
|
|
162
|
+
|
|
163
|
+
## Checks
|
|
164
|
+
|
|
165
|
+
Run the smallest relevant command for the changed area, such as:
|
|
166
|
+
|
|
167
|
+
- typecheck
|
|
168
|
+
- lint
|
|
169
|
+
- targeted tests
|
|
170
|
+
- Vite build when deployment shape could be affected
|
|
171
|
+
- SWA CLI smoke test when `staticwebapp.config.json` or `/api` routing changes
|
|
172
|
+
|
|
173
|
+
Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
|
|
174
|
+
|
|
175
|
+
## Figma MCP
|
|
176
|
+
|
|
177
|
+
When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
|
|
178
|
+
|
|
179
|
+
- Ensure responsiveness on all screen sizes
|
|
180
|
+
- If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
|
|
181
|
+
- Use the exact colours in the design. Don't make up your own.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -1,131 +1,243 @@
|
|
|
1
1
|
## Purpose
|
|
2
2
|
|
|
3
|
-
This is a Dynamics 365 / Dataverse web resource
|
|
4
|
-
Treat it as a **Dynamics-hosted frontend** — not a generic SPA. Do not modernise it into something else unless explicitly asked.
|
|
3
|
+
This repository is a Dynamics 365 / Dataverse web resource using React, TypeScript, and Vite.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Treat it as a Dynamics-hosted frontend, not a generic SPA. Keep it small, readable, and easy to ship into Dynamics.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Use the global AGENTS.md rules first. This file adds project-specific constraints.
|
|
9
8
|
|
|
10
9
|
## Hard Constraints
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
- Keep Dynamics runtime support working.
|
|
12
|
+
- Keep local development support working.
|
|
13
|
+
- Keep build output web-resource-friendly.
|
|
14
|
+
- Keep the app client-side.
|
|
15
|
+
- Make surgical changes.
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Do not modernize the project into a different architecture unless explicitly asked.
|
|
19
18
|
|
|
20
19
|
## Runtime Modes
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
The app supports two modes.
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
### Dynamics-hosted
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
- Detect Dynamics through the existing `window.parent.Xrm` / `window.top.Xrm` pattern.
|
|
26
|
+
- Derive the base URL from `Xrm.Utility.getGlobalContext().getClientUrl()`.
|
|
27
|
+
- Do not add bearer-token auth in hosted mode.
|
|
28
|
+
- Preserve `ClientGlobalContext.js.aspx` where the project uses it.
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
### Local dev
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
- Use `token.json` for local auth only.
|
|
33
|
+
- Load `token.json` dynamically.
|
|
34
|
+
- Never commit real token values.
|
|
35
|
+
- Never bundle `token.json` into deployment output.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
Do not mix the two modes. Do not duplicate runtime detection. Reuse `AuthService.ts`.
|
|
38
|
+
|
|
39
|
+
## Critical Files
|
|
40
|
+
|
|
41
|
+
| File | Rule |
|
|
33
42
|
|---|---|
|
|
34
|
-
| `src/services/
|
|
35
|
-
| `src/main.tsx` |
|
|
36
|
-
| `vite.config.ts` |
|
|
37
|
-
| `index.html` | Dynamics integration boundary
|
|
38
|
-
| `token.json` | Local dev only
|
|
43
|
+
| `src/services/AuthService.ts` | Single source of truth for runtime detection, base URL, and auth headers. |
|
|
44
|
+
| `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
|
|
45
|
+
| `vite.config.ts` | Preserve Dynamics-friendly output: `base: "./"`, predictable filenames, and `main.css`. |
|
|
46
|
+
| `index.html` | Treat as the Dynamics integration boundary. Preserve `ClientGlobalContext.js.aspx` where present. |
|
|
47
|
+
| `token.json` | Local dev only. Never commit real values or bundle it. |
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
## API and Data Access
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
Prefer direct, boring Dataverse Web API calls.
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
- Always reuse `getApiUrl()` and `getAuthHeaders()` — never duplicate them
|
|
46
|
-
- Use narrow `$select` queries; throw on non-OK responses
|
|
47
|
-
- No raw fetch calls scattered across UI components
|
|
48
|
-
- Use Zod for validation where data crosses a boundary: form inputs, URL/search params, config, and Dataverse/API responses that the UI depends on
|
|
49
|
-
- Keep service functions boring and explicit: one fetch function, one hook, one mutation where needed
|
|
50
|
-
- Do not add repository/client layers unless the app has enough repeated API logic to justify them
|
|
53
|
+
Use:
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
- `getApiUrl()`
|
|
56
|
+
- `getAuthHeaders()`
|
|
57
|
+
- narrow `$select` queries
|
|
58
|
+
- `URLSearchParams` for normal query parameters
|
|
59
|
+
- direct `fetch` inside service files
|
|
60
|
+
- small TypeScript interfaces for response shapes
|
|
61
|
+
- clear `response.ok` checks with useful status text
|
|
62
|
+
|
|
63
|
+
Avoid:
|
|
64
|
+
|
|
65
|
+
- raw fetch calls inside UI components
|
|
66
|
+
- duplicated auth or base URL logic
|
|
67
|
+
- repository/client layers for small features
|
|
68
|
+
- metadata resolvers unless arbitrary table names are a real requirement
|
|
69
|
+
- entity caches unless repeated metadata/API cost is demonstrated
|
|
70
|
+
- generic OData builders
|
|
71
|
+
- Zod schemas for every Dataverse response
|
|
72
|
+
- GUID or logical-name regex validation by default
|
|
73
|
+
- silent fallbacks for failed Dataverse calls
|
|
74
|
+
|
|
75
|
+
For known tables, use known entity set names. Fetch metadata only when the feature truly supports arbitrary entity logical names.
|
|
76
|
+
|
|
77
|
+
Escape OData string literals when interpolating inside quoted OData expressions. Do not create a broad escaping/parsing layer for simple queries.
|
|
78
|
+
|
|
79
|
+
## Validation
|
|
80
|
+
|
|
81
|
+
Use TypeScript types for normal Dataverse response shapes.
|
|
82
|
+
|
|
83
|
+
Use runtime validation only when the current feature needs it, such as:
|
|
84
|
+
|
|
85
|
+
- user-entered form data
|
|
86
|
+
- URL/search parameters that control behavior
|
|
87
|
+
- local config that can be wrong
|
|
88
|
+
- genuinely variable API data where the UI must branch safely
|
|
89
|
+
- security-sensitive or data-loss-prone paths
|
|
90
|
+
|
|
91
|
+
Do not validate values just because they are shaped like GUIDs, logical names, dates, URLs, or enum strings. If Dataverse will reject the value clearly and there is no local UX/security need, pass it through.
|
|
92
|
+
|
|
93
|
+
Do not normalize strings by default. Trim, lowercase, strip braces, or reformat only when the app has a known input source that sends multiple formats.
|
|
94
|
+
|
|
95
|
+
## Services, Queries, and Mutations
|
|
96
|
+
|
|
97
|
+
Keep service files explicit.
|
|
98
|
+
|
|
99
|
+
Preferred shape:
|
|
100
|
+
|
|
101
|
+
- one fetch/save function for the operation
|
|
102
|
+
- one TanStack Query hook when components need it
|
|
103
|
+
- one mutation hook when mutation state or invalidation is needed
|
|
104
|
+
- query keys colocated with the hook when reused for invalidation
|
|
105
|
+
|
|
106
|
+
Do not create wrapper chains such as:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
resolveConfig -> normalizeInput -> validateInput -> resolveMetadata -> buildRequest -> executeRequest
|
|
56
110
|
```
|
|
57
111
|
|
|
58
|
-
|
|
112
|
+
Prefer direct flow:
|
|
59
113
|
|
|
60
|
-
|
|
114
|
+
```text
|
|
115
|
+
read config -> fetch -> check response -> return typed data
|
|
116
|
+
```
|
|
61
117
|
|
|
62
118
|
## State Management
|
|
63
119
|
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
---
|
|
120
|
+
- Use TanStack Query for server state.
|
|
121
|
+
- Use local component state for local UI behavior.
|
|
122
|
+
- Use Zustand only for shared client state that has outgrown local state.
|
|
123
|
+
- Do not store server state in Zustand.
|
|
124
|
+
- Do not add Redux unless explicitly requested.
|
|
70
125
|
|
|
71
|
-
## UI
|
|
126
|
+
## UI and Styling
|
|
72
127
|
|
|
73
|
-
|
|
74
|
-
- If the project uses Shadcn/ui, use Shadcn components from `@/components/ui` and style them with Tailwind utility classes.
|
|
75
|
-
- If the project uses Kendo UI, use Kendo React components for controls, grids, menus, dialogs, inputs, and other rich UI. Use Tailwind for layout, spacing, and local composition around those components.
|
|
76
|
-
- Tailwind is the default styling approach for both Shadcn/ui and Kendo projects; preserve `main.css` output.
|
|
77
|
-
- Do not hand-roll component styling or custom CSS unless Tailwind/component props cannot reasonably express the requirement.
|
|
78
|
-
- Prefer existing component APIs, theme tokens, variants, and utility helpers before creating new wrappers.
|
|
79
|
-
- Keep screen layouts practical for embedded Dynamics use: compact, scannable, responsive, and not marketing-page styled.
|
|
128
|
+
Stay consistent with the project's existing UI system.
|
|
80
129
|
|
|
81
|
-
|
|
130
|
+
- Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
|
|
131
|
+
- Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
|
|
132
|
+
- Preserve the existing theme and `main.css` output.
|
|
133
|
+
- Do not mix UI systems unless explicitly asked.
|
|
134
|
+
- Do not hand-roll custom CSS unless component props and Tailwind are not enough.
|
|
135
|
+
- Keep layouts compact, scannable, responsive, and suitable for embedded Dynamics screens.
|
|
82
136
|
|
|
83
137
|
## Code Shape
|
|
84
138
|
|
|
85
|
-
|
|
86
|
-
- Avoid over-engineering: no generic frameworks, broad factories, speculative abstractions, or excessive configuration for small features.
|
|
87
|
-
- Avoid being overly defensive. Validate real external inputs and API responses where useful, but don't wrap every local value in ceremony.
|
|
88
|
-
- Prefer direct, typed functions over classes unless the existing code already uses classes for that concern.
|
|
89
|
-
- Keep React components focused: UI in components, reusable data access in services, shared client state in Zustand only when local state is no longer enough.
|
|
90
|
-
- Make the smallest change that solves the request cleanly.
|
|
139
|
+
Prefer:
|
|
91
140
|
|
|
92
|
-
|
|
141
|
+
- focused React components
|
|
142
|
+
- direct typed functions
|
|
143
|
+
- existing services and components
|
|
144
|
+
- small local helpers only when they remove real duplication or name non-obvious domain logic
|
|
145
|
+
- explicit Dataverse table/field handling over generic frameworks
|
|
93
146
|
|
|
94
|
-
|
|
147
|
+
Avoid:
|
|
95
148
|
|
|
96
|
-
|
|
149
|
+
- broad factories
|
|
150
|
+
- generic service clients
|
|
151
|
+
- classes for simple service logic
|
|
152
|
+
- excessive configuration
|
|
153
|
+
- defensive wrappers around every value
|
|
154
|
+
- broad refactors while adding a feature
|
|
97
155
|
|
|
98
|
-
|
|
156
|
+
## Error Handling
|
|
157
|
+
|
|
158
|
+
Dataverse reads, saves, deletes, uploads, downloads, auth failures, and required parsing failures should throw.
|
|
159
|
+
|
|
160
|
+
Do not swallow failed fetches and treat them as “not found” unless the requirement explicitly says the feature is best-effort.
|
|
161
|
+
|
|
162
|
+
Include response status and useful response text where practical.
|
|
163
|
+
|
|
164
|
+
## Build and Deployment
|
|
165
|
+
|
|
166
|
+
Do not replace Vite, add SSR, add Next.js, change output names, enable uncontrolled chunking, or introduce backend coupling unless explicitly asked.
|
|
167
|
+
|
|
168
|
+
Keep output deployable through Webresource Manager or the existing pipeline.
|
|
169
|
+
|
|
170
|
+
## Checks
|
|
171
|
+
|
|
172
|
+
Run the smallest relevant command for the changed area, such as:
|
|
173
|
+
|
|
174
|
+
- typecheck
|
|
175
|
+
- lint
|
|
176
|
+
- targeted tests
|
|
177
|
+
- Vite build when deployment shape could be affected
|
|
178
|
+
|
|
179
|
+
Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
|
|
99
180
|
|
|
100
181
|
## Example Service Pattern
|
|
101
182
|
|
|
102
183
|
```ts
|
|
103
|
-
import { getApiUrl, getAuthHeaders } from "@/services/authService";
|
|
104
184
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
105
185
|
|
|
186
|
+
import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
|
|
187
|
+
|
|
106
188
|
export interface Account {
|
|
107
189
|
accountid: string;
|
|
108
190
|
name?: string | null;
|
|
109
191
|
}
|
|
110
192
|
|
|
111
193
|
export const listAccounts = async (): Promise<Account[]> => {
|
|
112
|
-
const
|
|
194
|
+
const response = await fetch(
|
|
113
195
|
`${getApiUrl()}/accounts?$select=accountid,name&$top=50`,
|
|
114
196
|
{ headers: await getAuthHeaders() },
|
|
115
197
|
);
|
|
116
|
-
|
|
117
|
-
|
|
198
|
+
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(`Failed to fetch accounts: ${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const data = (await response.json()) as { value: Account[] };
|
|
204
|
+
return data.value;
|
|
118
205
|
};
|
|
119
206
|
|
|
207
|
+
const accountsQueryKey = ["accounts"] as const;
|
|
208
|
+
|
|
120
209
|
export const useAccounts = () =>
|
|
121
|
-
useQuery({ queryKey:
|
|
210
|
+
useQuery({ queryKey: accountsQueryKey, queryFn: listAccounts });
|
|
211
|
+
|
|
212
|
+
export const patchAccount = async (
|
|
213
|
+
id: string,
|
|
214
|
+
data: Partial<Account>,
|
|
215
|
+
): Promise<void> => {
|
|
216
|
+
const response = await fetch(`${getApiUrl()}/accounts(${id})`, {
|
|
217
|
+
method: "PATCH",
|
|
218
|
+
headers: await getAuthHeaders(),
|
|
219
|
+
body: JSON.stringify(data),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
throw new Error(`Failed to update account: ${response.status}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
122
226
|
|
|
123
227
|
export const useUpdateAccount = () => {
|
|
124
228
|
const queryClient = useQueryClient();
|
|
229
|
+
|
|
125
230
|
return useMutation({
|
|
126
231
|
mutationFn: ({ id, data }: { id: string; data: Partial<Account> }) =>
|
|
127
232
|
patchAccount(id, data),
|
|
128
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey:
|
|
233
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: accountsQueryKey }),
|
|
129
234
|
});
|
|
130
235
|
};
|
|
131
236
|
```
|
|
237
|
+
|
|
238
|
+
## Figma MCP
|
|
239
|
+
When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
|
|
240
|
+
|
|
241
|
+
- Ensure responsiveness on all screen sizes
|
|
242
|
+
- If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
|
|
243
|
+
- Use the exact colours in the design. Don't make up your own.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -30,7 +30,7 @@ React + TypeScript template for Dynamics 365/Dataverse web resources. Generated
|
|
|
30
30
|
## Key Files
|
|
31
31
|
|
|
32
32
|
- `src/main.tsx`: Sets up React, Tailwind, and TanStack Query. If Kendo UI was selected, imports the chosen theme CSS (`<theme>/dist/all.css`).
|
|
33
|
-
- `src/services/
|
|
33
|
+
- `src/services/AuthService.ts`: Utilities to build API URLs and headers based on environment (inside Dynamics vs. local dev).
|
|
34
34
|
- `token.json`: Local development token store. Build is configured to treat this as external and not bundle it.
|
|
35
35
|
- `index.html`: Injects `ClientGlobalContext.js.aspx` for Dynamics runtime.
|
|
36
36
|
- `vite.config.ts`: Uses base `./`, disables code splitting, emits `main.css`, and places assets at the top of `dist`.
|
|
@@ -40,7 +40,7 @@ React + TypeScript template for Dynamics 365/Dataverse web resources. Generated
|
|
|
40
40
|
The app auto-detects whether it runs inside Dynamics 365 (uses `window.Xrm` and does not add an Authorization header) or locally (reads a bearer token from `token.json`).
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
-
// src/services/
|
|
43
|
+
// src/services/AuthService.ts
|
|
44
44
|
export const getApiUrl = (): string => {
|
|
45
45
|
if (window.parent && window.parent.Xrm) {
|
|
46
46
|
const clientUrl = window.Xrm.Utility.getGlobalContext().getClientUrl();
|
|
@@ -72,7 +72,7 @@ export const getAuthHeaders = async (): Promise<HeadersInit> => {
|
|
|
72
72
|
Example usage:
|
|
73
73
|
|
|
74
74
|
```ts
|
|
75
|
-
import { getApiUrl, getAuthHeaders } from "@/services/
|
|
75
|
+
import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
|
|
76
76
|
|
|
77
77
|
const res = await fetch(`${getApiUrl()}/accounts?$top=10`, {
|
|
78
78
|
headers: await getAuthHeaders(),
|
|
@@ -139,7 +139,7 @@ The following example shows a minimal data service and hooks to fetch and update
|
|
|
139
139
|
Create `src/services/accounts.ts`:
|
|
140
140
|
|
|
141
141
|
```ts
|
|
142
|
-
import { getApiUrl, getAuthHeaders } from "@/services/
|
|
142
|
+
import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
|
|
143
143
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
144
144
|
|
|
145
145
|
export interface Account {
|
|
@@ -232,7 +232,7 @@ export function AccountsList() {
|
|
|
232
232
|
<Button>Click me</Button>;
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
- Shadcn/ui: Components are
|
|
235
|
+
- Shadcn/ui: Components are copied from the committed template snapshot and available under the `@/components` alias. Example:
|
|
236
236
|
|
|
237
237
|
```tsx
|
|
238
238
|
import { Button } from "@/components/ui/button";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# shadcn Template Snapshot
|
|
2
|
+
|
|
3
|
+
This template contains committed shadcn component source.
|
|
4
|
+
|
|
5
|
+
Generated with:
|
|
6
|
+
|
|
7
|
+
- shadcn CLI: 4.12.0
|
|
8
|
+
- style: radix-nova
|
|
9
|
+
- base color: neutral
|
|
10
|
+
- Tailwind: v4
|
|
11
|
+
- generated by: `npm run refresh:shadcn-template`
|
|
12
|
+
|
|
13
|
+
Normal `create-ec-app` scaffolding does not run `npx shadcn`.
|
|
14
|
+
To refresh this snapshot, run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm run refresh:shadcn-template
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
After refreshing, generate a test app and run a build before committing.
|