create-ec-app 0.0.6 → 0.0.7
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/package.json +3 -2
- package/templates/base/.prettierrc.json +12 -0
- package/templates/base/README.md +73 -0
- package/templates/base/biome.json +54 -0
- package/templates/base/eslint.config.js +23 -0
- package/templates/base/index.html +13 -0
- package/templates/base/package-lock.json +3898 -0
- package/templates/base/package.json +42 -0
- package/templates/base/public/vite.svg +1 -0
- package/templates/base/src/App.css +0 -0
- package/templates/base/src/App.tsx +11 -0
- package/templates/base/src/global.d.ts +4 -0
- package/templates/base/src/index.css +1 -0
- package/templates/base/src/main.tsx +29 -0
- package/templates/base/tsconfig.app.json +32 -0
- package/templates/base/tsconfig.json +19 -0
- package/templates/base/tsconfig.node.json +26 -0
- package/templates/base/vite.config.ts +14 -0
- package/templates/targets/power-pages/.env +3 -0
- package/templates/targets/power-pages/README.md +180 -0
- package/templates/targets/power-pages/powerpages.config.json +5 -0
- package/templates/targets/power-pages/src/App.patch.tsx +17 -0
- package/templates/targets/power-pages/src/context/AuthContext.tsx +273 -0
- package/templates/targets/power-pages/src/main.patch.tsx +32 -0
- package/templates/targets/power-pages/vite.config.patch.ts +23 -0
- package/templates/targets/swa/.env +2 -0
- package/templates/targets/swa/package.patch.json +5 -0
- package/templates/targets/swa/staticwebapp.config.json +5 -0
- package/templates/targets/swa/swa-cli.config.json +14 -0
- package/templates/targets/webresource/README.md +263 -0
- package/templates/targets/webresource/src/services/AuthService.ts +38 -0
- package/templates/targets/webresource/token.json +8 -0
- package/templates/targets/webresource/vite.config.patch.ts +40 -0
- package/templates/ui/kendo/kendo-tw-preset.js +240 -0
- package/templates/ui/kendo/package.patch.json +7 -0
- package/templates/ui/kendo/src/main.patch.tsx +29 -0
- package/templates/ui/kendo/tailwind.config.js +14 -0
- package/templates/ui/shadcn-ui/components.json +22 -0
- package/templates/ui/shadcn-ui/package.patch.json +50 -0
- package/templates/ui/shadcn-ui/src/components/ui/accordion.tsx +64 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert-dialog.tsx +155 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert.tsx +66 -0
- package/templates/ui/shadcn-ui/src/components/ui/aspect-ratio.tsx +11 -0
- package/templates/ui/shadcn-ui/src/components/ui/avatar.tsx +51 -0
- package/templates/ui/shadcn-ui/src/components/ui/badge.tsx +46 -0
- package/templates/ui/shadcn-ui/src/components/ui/breadcrumb.tsx +109 -0
- package/templates/ui/shadcn-ui/src/components/ui/button-group.tsx +83 -0
- package/templates/ui/shadcn-ui/src/components/ui/button.tsx +60 -0
- package/templates/ui/shadcn-ui/src/components/ui/calendar.tsx +216 -0
- package/templates/ui/shadcn-ui/src/components/ui/card.tsx +92 -0
- package/templates/ui/shadcn-ui/src/components/ui/carousel.tsx +239 -0
- package/templates/ui/shadcn-ui/src/components/ui/chart.tsx +357 -0
- package/templates/ui/shadcn-ui/src/components/ui/checkbox.tsx +32 -0
- package/templates/ui/shadcn-ui/src/components/ui/collapsible.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/command.tsx +182 -0
- package/templates/ui/shadcn-ui/src/components/ui/context-menu.tsx +252 -0
- package/templates/ui/shadcn-ui/src/components/ui/dialog.tsx +141 -0
- package/templates/ui/shadcn-ui/src/components/ui/drawer.tsx +135 -0
- package/templates/ui/shadcn-ui/src/components/ui/dropdown-menu.tsx +255 -0
- package/templates/ui/shadcn-ui/src/components/ui/empty.tsx +104 -0
- package/templates/ui/shadcn-ui/src/components/ui/field.tsx +246 -0
- package/templates/ui/shadcn-ui/src/components/ui/form.tsx +167 -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 +170 -0
- package/templates/ui/shadcn-ui/src/components/ui/input-otp.tsx +75 -0
- package/templates/ui/shadcn-ui/src/components/ui/input.tsx +21 -0
- package/templates/ui/shadcn-ui/src/components/ui/item.tsx +193 -0
- package/templates/ui/shadcn-ui/src/components/ui/kbd.tsx +28 -0
- package/templates/ui/shadcn-ui/src/components/ui/label.tsx +24 -0
- package/templates/ui/shadcn-ui/src/components/ui/menubar.tsx +274 -0
- package/templates/ui/shadcn-ui/src/components/ui/navigation-menu.tsx +168 -0
- package/templates/ui/shadcn-ui/src/components/ui/pagination.tsx +127 -0
- package/templates/ui/shadcn-ui/src/components/ui/popover.tsx +48 -0
- package/templates/ui/shadcn-ui/src/components/ui/progress.tsx +29 -0
- package/templates/ui/shadcn-ui/src/components/ui/radio-group.tsx +45 -0
- package/templates/ui/shadcn-ui/src/components/ui/resizable.tsx +54 -0
- package/templates/ui/shadcn-ui/src/components/ui/scroll-area.tsx +58 -0
- package/templates/ui/shadcn-ui/src/components/ui/select.tsx +185 -0
- package/templates/ui/shadcn-ui/src/components/ui/separator.tsx +28 -0
- package/templates/ui/shadcn-ui/src/components/ui/sheet.tsx +137 -0
- package/templates/ui/shadcn-ui/src/components/ui/sidebar.tsx +726 -0
- package/templates/ui/shadcn-ui/src/components/ui/skeleton.tsx +13 -0
- package/templates/ui/shadcn-ui/src/components/ui/slider.tsx +63 -0
- package/templates/ui/shadcn-ui/src/components/ui/sonner.tsx +38 -0
- package/templates/ui/shadcn-ui/src/components/ui/spinner.tsx +16 -0
- package/templates/ui/shadcn-ui/src/components/ui/switch.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/table.tsx +114 -0
- package/templates/ui/shadcn-ui/src/components/ui/tabs.tsx +66 -0
- package/templates/ui/shadcn-ui/src/components/ui/textarea.tsx +18 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle-group.tsx +81 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle.tsx +45 -0
- package/templates/ui/shadcn-ui/src/components/ui/tooltip.tsx +61 -0
- package/templates/ui/shadcn-ui/src/hooks/use-mobile.ts +19 -0
- package/templates/ui/shadcn-ui/src/index.patch.css +121 -0
- package/templates/ui/shadcn-ui/src/lib/utils.ts +6 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { StrictMode } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
|
|
5
|
+
import "./index.css";
|
|
6
|
+
import App from "./App.tsx";
|
|
7
|
+
import { AuthProvider } from "./context/AuthContext.tsx";
|
|
8
|
+
|
|
9
|
+
const queryClient = new QueryClient({
|
|
10
|
+
defaultOptions: {
|
|
11
|
+
queries: {
|
|
12
|
+
refetchOnWindowFocus: false,
|
|
13
|
+
retry: 3,
|
|
14
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
15
|
+
},
|
|
16
|
+
mutations: {
|
|
17
|
+
retry: 1,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const root = createRoot(document.getElementById("root")!);
|
|
23
|
+
|
|
24
|
+
root.render(
|
|
25
|
+
<StrictMode>
|
|
26
|
+
<AuthProvider>
|
|
27
|
+
<QueryClientProvider client={queryClient}>
|
|
28
|
+
<App />
|
|
29
|
+
</QueryClientProvider>
|
|
30
|
+
</AuthProvider>
|
|
31
|
+
</StrictMode>
|
|
32
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { defineConfig } from "vite";
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [react(), tailwindcss()],
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
"@": path.resolve(__dirname, "./src"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
server: {
|
|
15
|
+
proxy: {
|
|
16
|
+
"/_api": {
|
|
17
|
+
target: "https://pp-01.powerappsportals.com",
|
|
18
|
+
changeOrigin: true,
|
|
19
|
+
secure: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://aka.ms/azure/static-web-apps-cli/schema",
|
|
3
|
+
"configurations": {
|
|
4
|
+
"swa-app": {
|
|
5
|
+
"appLocation": ".",
|
|
6
|
+
"outputLocation": "dist",
|
|
7
|
+
"appBuildCommand": "npm run build",
|
|
8
|
+
"run": "npm run dev",
|
|
9
|
+
"appDevserverUrl": "http://localhost:5173",
|
|
10
|
+
"appName": "swa-app",
|
|
11
|
+
"resourceGroup": "swa-app-rg"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# EC Webresource App
|
|
2
|
+
|
|
3
|
+
React + TypeScript template for Dynamics 365/Dataverse web resources. Generated by `create-ec-app`, it provides a lean setup with Vite, Tailwind CSS, and a choice of UI library (Kendo UI or Shadcn/ui), pre-configured for running inside Dynamics 365 and for local development.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- React + TypeScript with Vite (Client side only)
|
|
8
|
+
- Tailwind CSS configured out of the box
|
|
9
|
+
- UI library choice: Kendo UI (with theme selection) or Shadcn/ui
|
|
10
|
+
- TanStack Query provider pre-wired (`QueryClientProvider` in `src/main.tsx`)
|
|
11
|
+
- XRM-aware runtime: adds `ClientGlobalContext.js.aspx` and detects `window.Xrm`
|
|
12
|
+
- Local development via `token.json` (excluded from bundling) and helper functions
|
|
13
|
+
- Vite build tuned for web resources: single JS bundle, `main.css`, deterministic names
|
|
14
|
+
- Zustand and `@types/xrm` included for state and typings
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- Node.js 22+ and npm 9+
|
|
19
|
+
- Dynamics 365/Dataverse environment for deployment
|
|
20
|
+
- Kendo UI license (only if you picked Kendo UI)
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
- Install dependencies (if needed): `npm install`
|
|
25
|
+
- For Kendo UI projects, activate your license: `npx kendo-ui-license activate` (you need to copy over your kendo-license.txt to the root to activate or stick with non-premium components)
|
|
26
|
+
- Start dev server: `npm run dev`
|
|
27
|
+
- Production build: `npm run build`
|
|
28
|
+
- Development build (readable, no minify): `npm run build:dev`
|
|
29
|
+
|
|
30
|
+
## Key Files
|
|
31
|
+
|
|
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/authService.ts`: Utilities to build API URLs and headers based on environment (inside Dynamics vs. local dev).
|
|
34
|
+
- `token.json`: Local development token store. Build is configured to treat this as external and not bundle it.
|
|
35
|
+
- `index.html`: Injects `ClientGlobalContext.js.aspx` for Dynamics runtime.
|
|
36
|
+
- `vite.config.ts`: Uses base `./`, disables code splitting, emits `main.css`, and places assets at the top of `dist`.
|
|
37
|
+
|
|
38
|
+
## Auth and API Access
|
|
39
|
+
|
|
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
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// src/services/authService.ts
|
|
44
|
+
export const getApiUrl = (): string => {
|
|
45
|
+
if (window.parent && window.parent.Xrm) {
|
|
46
|
+
const clientUrl = window.Xrm.Utility.getGlobalContext().getClientUrl();
|
|
47
|
+
return `${clientUrl}/api/data/v9.2`;
|
|
48
|
+
}
|
|
49
|
+
return "https://DOMAIN.REGION.dynamics.com/api/data/v9.2";
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const getAuthHeaders = async (): Promise<HeadersInit> => {
|
|
53
|
+
if (window.parent && window.parent.Xrm) {
|
|
54
|
+
return {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"OData-MaxVersion": "4.0",
|
|
57
|
+
"OData-Version": "4.0",
|
|
58
|
+
Prefer: 'odata.include-annotations="*"',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const { default: token } = await import("../../token.json");
|
|
62
|
+
return {
|
|
63
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
"OData-MaxVersion": "4.0",
|
|
66
|
+
"OData-Version": "4.0",
|
|
67
|
+
Prefer: 'odata.include-annotations="*"',
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Example usage:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { getApiUrl, getAuthHeaders } from "@/services/authService";
|
|
76
|
+
|
|
77
|
+
const res = await fetch(`${getApiUrl()}/accounts?$top=10`, {
|
|
78
|
+
headers: await getAuthHeaders(),
|
|
79
|
+
});
|
|
80
|
+
const data = await res.json();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Azure CLI: Generate a Dataverse Token (Local Dev)
|
|
84
|
+
|
|
85
|
+
Use Azure CLI to obtain a bearer token for your Dataverse environment and paste it into `token.json` for local development.
|
|
86
|
+
|
|
87
|
+
Prerequisites:
|
|
88
|
+
|
|
89
|
+
- Azure CLI installed and you have access to the target tenant and environment.
|
|
90
|
+
|
|
91
|
+
Steps:
|
|
92
|
+
|
|
93
|
+
1. Sign in to Azure (optionally targeting a specific tenant):
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
az login --tenant <tenant-id>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
2. Request an access token for your Dataverse environment URL (replace `<org>` and domain as applicable):
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
az account get-access-token \
|
|
103
|
+
--tenant <tenant-id> \
|
|
104
|
+
--resource https://<org>.crm.dynamics.com \
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This prints the raw token to stdout. Copy it.
|
|
108
|
+
|
|
109
|
+
3. Paste the token value into `token.json` under `accessToken`:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
{
|
|
113
|
+
"accessToken": "<paste-token-here>",
|
|
114
|
+
"expiresIn": "",
|
|
115
|
+
"expires_on": 0,
|
|
116
|
+
"subscription": "",
|
|
117
|
+
"tenant": "",
|
|
118
|
+
"tokenType": "Bearer"
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Notes:
|
|
123
|
+
|
|
124
|
+
- Tokens expire; regenerate as needed. (Max expiry is 1 hour)
|
|
125
|
+
- If your CLI requires scopes, use `--scope https://<org>.crm.dynamics.com/.default` instead of `--resource`.
|
|
126
|
+
|
|
127
|
+
One‑liner (writes full JSON to `token.json`):
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
az account get-access-token --resource=https://<org>.crm.dynamics.com > token.json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Replace the URL with your environment’s Dataverse URL (e.g., `https://<org>.crm.dynamics.com`, `https://<org>.crm4.dynamics.com`, etc.). This overwrites `token.json` at the project root with the full output from Azure CLI, which includes `accessToken` used by the local dev flow.
|
|
134
|
+
|
|
135
|
+
## Accounts Data Service (TanStack Query)
|
|
136
|
+
|
|
137
|
+
The following example shows a minimal data service and hooks to fetch and update Accounts using the Dataverse Web API and TanStack Query.
|
|
138
|
+
|
|
139
|
+
Create `src/services/accounts.ts`:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { getApiUrl, getAuthHeaders } from "@/services/authService";
|
|
143
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
144
|
+
|
|
145
|
+
export interface Account {
|
|
146
|
+
accountid: string;
|
|
147
|
+
name?: string | null;
|
|
148
|
+
description?: string | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const listAccounts = async (): Promise<Account[]> => {
|
|
152
|
+
const res = await fetch(
|
|
153
|
+
`${getApiUrl()}/accounts?$select=accountid,name,description&$top=50`,
|
|
154
|
+
{ headers: await getAuthHeaders() },
|
|
155
|
+
);
|
|
156
|
+
if (!res.ok) throw new Error(`Failed to fetch accounts: ${res.status}`);
|
|
157
|
+
const json = await res.json();
|
|
158
|
+
return json.value as Account[];
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const patchAccount = async (
|
|
162
|
+
id: string,
|
|
163
|
+
data: Partial<Account>,
|
|
164
|
+
): Promise<void> => {
|
|
165
|
+
const headers = await getAuthHeaders();
|
|
166
|
+
const res = await fetch(`${getApiUrl()}/accounts(${id})`, {
|
|
167
|
+
method: "PATCH",
|
|
168
|
+
headers: {
|
|
169
|
+
...headers,
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify(data),
|
|
172
|
+
});
|
|
173
|
+
if (!res.ok) throw new Error(`Failed to update account: ${res.status}`);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const useAccounts = () =>
|
|
177
|
+
useQuery({ queryKey: ["accounts"], queryFn: listAccounts });
|
|
178
|
+
|
|
179
|
+
export const useUpdateAccount = () => {
|
|
180
|
+
const qc = useQueryClient();
|
|
181
|
+
return useMutation({
|
|
182
|
+
mutationFn: ({ id, data }: { id: string; data: Partial<Account> }) =>
|
|
183
|
+
patchAccount(id, data),
|
|
184
|
+
onSuccess: () => {
|
|
185
|
+
qc.invalidateQueries({ queryKey: ["accounts"] });
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Usage in a component (e.g. `src/AccountsList.tsx`):
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { useAccounts, useUpdateAccount } from "@/services/accounts";
|
|
195
|
+
|
|
196
|
+
export function AccountsList() {
|
|
197
|
+
const { data, isLoading, error } = useAccounts();
|
|
198
|
+
const updateAccount = useUpdateAccount();
|
|
199
|
+
|
|
200
|
+
if (isLoading) return <div>Loading…</div>;
|
|
201
|
+
if (error) return <div>Failed to load accounts</div>;
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<ul className="space-y-2">
|
|
205
|
+
{data?.map((a) => (
|
|
206
|
+
<li key={a.accountid} className="flex items-center gap-2">
|
|
207
|
+
<span className="flex-1">{a.name ?? "(no name)"}</span>
|
|
208
|
+
<button
|
|
209
|
+
className="px-2 py-1 border rounded"
|
|
210
|
+
onClick={() =>
|
|
211
|
+
updateAccount.mutate({
|
|
212
|
+
id: a.accountid,
|
|
213
|
+
data: { name: `${a.name ?? ""}*` },
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
>
|
|
217
|
+
Rename
|
|
218
|
+
</button>
|
|
219
|
+
</li>
|
|
220
|
+
))}
|
|
221
|
+
</ul>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## UI Libraries
|
|
227
|
+
|
|
228
|
+
- Kendo UI: Theme CSS is imported in `src/main.tsx`. Example:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { Button } from "@progress/kendo-react-buttons";
|
|
232
|
+
<Button>Click me</Button>;
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- Shadcn/ui: Components are installed and available under the `@/components` alias. Example:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { Button } from "@/components/ui/button";
|
|
239
|
+
<Button>Click me</Button>;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Build Output
|
|
243
|
+
|
|
244
|
+
- Output directory: `dist`
|
|
245
|
+
- Single JavaScript bundle with deterministic name
|
|
246
|
+
- Single CSS file: `main.css`
|
|
247
|
+
- `base: "./"` to support deployment as a web resource
|
|
248
|
+
|
|
249
|
+
## Deployment
|
|
250
|
+
|
|
251
|
+
- Upload files from `dist` to Dynamics 365/Dataverse as web resources.
|
|
252
|
+
- Use `index.html` as the HTML web resource; upload the JS bundle and `main.css` as script/style web resources referenced by it.
|
|
253
|
+
- Consider automating uploads with your preferred tooling (DevOps pipelines, XrmToolBox, etc.).
|
|
254
|
+
- One quick and easy way to handle deployment is with Webresource Manager.
|
|
255
|
+
- Open up webresource manager, and navigate to your specific solution
|
|
256
|
+
- Create a new root (example GlobalAccounts*or CustomDev*)
|
|
257
|
+
- Add a new folder for your webresources
|
|
258
|
+
- Upload your index.html, index.js and main.css to your folder.
|
|
259
|
+
- This will now allow you to use auto publisher to bind to your deployed resources.
|
|
260
|
+
|
|
261
|
+
## Notes
|
|
262
|
+
|
|
263
|
+
- If you change the build, ensure code splitting stays disabled and asset names remain predictable to simplify web resource updates.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
const getAuthToken = async (): Promise<string> => {
|
|
3
|
+
const tokenModule = await import("../../token.json");
|
|
4
|
+
const token = tokenModule.default.accessToken;
|
|
5
|
+
return token;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const getApiUrl = (): string => {
|
|
9
|
+
if (window.parent && window.parent.Xrm) {
|
|
10
|
+
const globalContext = window.Xrm.Utility.getGlobalContext();
|
|
11
|
+
const clientUrl = globalContext.getClientUrl();
|
|
12
|
+
return `${clientUrl}/api/data/v9.2`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return "https://DOMAIN.REGION.dynamics.com/api/data/v9.2";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getAuthHeaders = async (): Promise<HeadersInit> => {
|
|
19
|
+
if (window.parent && window.parent.Xrm) {
|
|
20
|
+
return {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
"OData-MaxVersion": "4.0",
|
|
23
|
+
"OData-Version": "4.0",
|
|
24
|
+
Prefer: 'odata.include-annotations="*"',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const token = await getAuthToken();
|
|
29
|
+
const headers: HeadersInit = {
|
|
30
|
+
Authorization: `Bearer ${token}`,
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
"OData-MaxVersion": "4.0",
|
|
33
|
+
"OData-Version": "4.0",
|
|
34
|
+
Prefer: 'odata.include-annotations="*"',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return headers;
|
|
38
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
3
|
+
import { defineConfig } from "vite";
|
|
4
|
+
import react from "@vitejs/plugin-react";
|
|
5
|
+
|
|
6
|
+
export default defineConfig(({ command, mode }) => {
|
|
7
|
+
const isDev = mode === "development" || command === "serve";
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
base: "./",
|
|
11
|
+
plugins: [react(), tailwindcss()],
|
|
12
|
+
resolve: {
|
|
13
|
+
alias: {
|
|
14
|
+
"@": path.resolve(__dirname, "./src"),
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
build: {
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
external: ["../../token.json"],
|
|
20
|
+
output: {
|
|
21
|
+
manualChunks: undefined,
|
|
22
|
+
entryFileNames: "[name].js",
|
|
23
|
+
chunkFileNames: "[name].chunk.js",
|
|
24
|
+
assetFileNames: (assetInfo) => {
|
|
25
|
+
if (assetInfo.name?.endsWith(".css")) {
|
|
26
|
+
return "main.css";
|
|
27
|
+
}
|
|
28
|
+
return "[name].[ext]";
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
minify: !isDev,
|
|
33
|
+
mode: isDev ? "development" : "production",
|
|
34
|
+
assetsDir: "",
|
|
35
|
+
target: "es2015",
|
|
36
|
+
cssCodeSplit: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
theme: {
|
|
3
|
+
extend: {
|
|
4
|
+
spacing: {
|
|
5
|
+
1: "var( --kendo-spacing-1 )",
|
|
6
|
+
1.5: "var( --kendo-spacing-1.5 )",
|
|
7
|
+
2: "var( --kendo-spacing-2 )",
|
|
8
|
+
2.5: "var( --kendo-spacing-2.5 )",
|
|
9
|
+
3: "var( --kendo-spacing-3 )",
|
|
10
|
+
3.5: "var( --kendo-spacing-3.5 )",
|
|
11
|
+
4: "var( --kendo-spacing-4 )",
|
|
12
|
+
4.5: "var( --kendo-spacing-4.5 )",
|
|
13
|
+
5: "var( --kendo-spacing-5 )",
|
|
14
|
+
5.5: "var( --kendo-spacing-5.5 )",
|
|
15
|
+
6: "var( --kendo-spacing-6 )",
|
|
16
|
+
6.5: "var( --kendo-spacing-6.5 )",
|
|
17
|
+
7: "var( --kendo-spacing-7 )",
|
|
18
|
+
7.5: "var( --kendo-spacing-7.5 )",
|
|
19
|
+
8: "var( --kendo-spacing-8 )",
|
|
20
|
+
9: "var( --kendo-spacing-9 )",
|
|
21
|
+
10: "var( --kendo-spacing-10 )",
|
|
22
|
+
11: "var( --kendo-spacing-11 )",
|
|
23
|
+
12: "var( --kendo-spacing-12 )",
|
|
24
|
+
13: "var( --kendo-spacing-13 )",
|
|
25
|
+
14: "var( --kendo-spacing-14 )",
|
|
26
|
+
15: "var( --kendo-spacing-15 )",
|
|
27
|
+
16: "var( --kendo-spacing-16 )",
|
|
28
|
+
17: "var( --kendo-spacing-17 )",
|
|
29
|
+
18: "var( --kendo-spacing-18 )",
|
|
30
|
+
19: "var( --kendo-spacing-19 )",
|
|
31
|
+
20: "var( --kendo-spacing-20 )",
|
|
32
|
+
21: "var( --kendo-spacing-21 )",
|
|
33
|
+
22: "var( --kendo-spacing-22 )",
|
|
34
|
+
23: "var( --kendo-spacing-23 )",
|
|
35
|
+
24: "var( --kendo-spacing-24 )",
|
|
36
|
+
25: "var( --kendo-spacing-25 )",
|
|
37
|
+
26: "var( --kendo-spacing-26 )",
|
|
38
|
+
27: "var( --kendo-spacing-27 )",
|
|
39
|
+
28: "var( --kendo-spacing-28 )",
|
|
40
|
+
29: "var( --kendo-spacing-29 )",
|
|
41
|
+
30: "var( --kendo-spacing-30 )",
|
|
42
|
+
},
|
|
43
|
+
borderRadius: {
|
|
44
|
+
none: "var( --kendo-border-radius-none )",
|
|
45
|
+
sm: "var( --kendo-border-radius-sm )",
|
|
46
|
+
DEFAULT: "var( --kendo-border-radius-md )",
|
|
47
|
+
lg: "var( --kendo-border-radius-lg )",
|
|
48
|
+
xl: "var( --kendo-border-radius-xl )",
|
|
49
|
+
"2xl": "var( --kendo-border-radius-xxl )",
|
|
50
|
+
"3xl": "var( --kendo-border-radius-xxxl )",
|
|
51
|
+
full: "var( --kendo-border-radius-none )",
|
|
52
|
+
},
|
|
53
|
+
boxShadow: {
|
|
54
|
+
sm: "var( --kendo-elevation-2 )",
|
|
55
|
+
DEFAULT: "var( --kendo-elevation-4 )",
|
|
56
|
+
lg: "var( --kendo-elevation-6 )",
|
|
57
|
+
xl: "var( --kendo-elevation-8 )",
|
|
58
|
+
"2xl": "var( --keno-elevation-9 )",
|
|
59
|
+
},
|
|
60
|
+
colors: {
|
|
61
|
+
"app-surface": "var( --kendo-color-app-surface )",
|
|
62
|
+
"on-app-surface": "var( --kendo-color-on-app-surface )",
|
|
63
|
+
subtle: "var( --kendo-color-subtle )",
|
|
64
|
+
surface: "var( --kendo-color-surface )",
|
|
65
|
+
"surface-alt": "var( --kendo-color-surface-alt )",
|
|
66
|
+
border: "var( --kendo-color-border )",
|
|
67
|
+
"border-alt": "var( --kendo-color-border-alt )",
|
|
68
|
+
base: {
|
|
69
|
+
DEFAULT: "var( --kendo-color-base )",
|
|
70
|
+
hover: "var( --kendo-color-base-hover )",
|
|
71
|
+
active: "var( --kendo-color-base-active )",
|
|
72
|
+
emphasis: "var( --kendo-color-base-emphasis )",
|
|
73
|
+
subtle: "var( --kendo-color-base-subtle )",
|
|
74
|
+
"subtle-hover": "var( --kendo-color-base-subtle-hover )",
|
|
75
|
+
"subtle-active": "var( --kendo-color-base-subtle-active)",
|
|
76
|
+
"on-subtle": "var( --kendo-color-base-on-subtle )",
|
|
77
|
+
"on-surface": "var( --kendo-color-base-on-surface )",
|
|
78
|
+
},
|
|
79
|
+
"on-base": "var( --kendo-color-on-base )",
|
|
80
|
+
primary: {
|
|
81
|
+
DEFAULT: "var( --kendo-color-primary )",
|
|
82
|
+
hover: "var( --kendo-color-primary-hover )",
|
|
83
|
+
active: "var( --kendo-color-primary-active )",
|
|
84
|
+
emphasis: "var( --kendo-color-primary-emphasis )",
|
|
85
|
+
subtle: "var( --kendo-color-primary-subtle )",
|
|
86
|
+
"subtle-hover": "var( --kendo-color-primary-subtle-hover )",
|
|
87
|
+
"subtle-active":
|
|
88
|
+
"var( --kendo-color-primary-subtle-active)",
|
|
89
|
+
"on-subtle": "var( --kendo-color-primary-on-subtle )",
|
|
90
|
+
"on-surface": "var( --kendo-color-primary-on-surface )",
|
|
91
|
+
},
|
|
92
|
+
"on-primary": "var( --kendo-color-on-primary )",
|
|
93
|
+
secondary: {
|
|
94
|
+
DEFAULT: "var( --kendo-color-secondary )",
|
|
95
|
+
hover: "var( --kendo-color-secondary-hover )",
|
|
96
|
+
active: "var( --kendo-color-secondary-active )",
|
|
97
|
+
emphasis: "var( --kendo-color-secondary-emphasis )",
|
|
98
|
+
subtle: "var( --kendo-color-secondary-subtle )",
|
|
99
|
+
"subtle-hover":
|
|
100
|
+
"var( --kendo-color-secondary-subtle-hover )",
|
|
101
|
+
"subtle-active":
|
|
102
|
+
"var( --kendo-color-secondary-subtle-active)",
|
|
103
|
+
"on-subtle": "var( --kendo-color-secondary-on-subtle )",
|
|
104
|
+
"on-surface": "var( --kendo-color-secondary-on-surface )",
|
|
105
|
+
},
|
|
106
|
+
"on-secondary": "var( --kendo-color-on-secondary )",
|
|
107
|
+
tertiary: {
|
|
108
|
+
DEFAULT: "var( --kendo-color-tertiary )",
|
|
109
|
+
hover: "var( --kendo-color-tertiary-hover )",
|
|
110
|
+
active: "var( --kendo-color-tertiary-active )",
|
|
111
|
+
emphasis: "var( --kendo-color-tertiary-emphasis )",
|
|
112
|
+
subtle: "var( --kendo-color-tertiary-subtle )",
|
|
113
|
+
"subtle-hover":
|
|
114
|
+
"var( --kendo-color-tertiary-subtle-hover )",
|
|
115
|
+
"subtle-active":
|
|
116
|
+
"var( --kendo-color-tertiary-subtle-active)",
|
|
117
|
+
"on-subtle": "var( --kendo-color-tertiary-on-subtle )",
|
|
118
|
+
"on-surface": "var( --kendo-color-tertiary-on-surface )",
|
|
119
|
+
},
|
|
120
|
+
"on-tertiary": "var( --kendo-color-on-tertiary )",
|
|
121
|
+
light: {
|
|
122
|
+
DEFAULT: "var( --kendo-color-light )",
|
|
123
|
+
hover: "var( --kendo-color-light-hover )",
|
|
124
|
+
active: "var( --kendo-color-light-active )",
|
|
125
|
+
emphasis: "var( --kendo-color-light-emphasis )",
|
|
126
|
+
subtle: "var( --kendo-color-light-subtle )",
|
|
127
|
+
"subtle-hover": "var( --kendo-color-light-subtle-hover )",
|
|
128
|
+
"subtle-active": "var( --kendo-color-light-subtle-active)",
|
|
129
|
+
"on-subtle": "var( --kendo-color-light-on-subtle )",
|
|
130
|
+
"on-surface": "var( --kendo-color-light-on-surface )",
|
|
131
|
+
},
|
|
132
|
+
"on-light": "var( --kendo-color-on-light )",
|
|
133
|
+
dark: {
|
|
134
|
+
DEFAULT: "var( --kendo-color-dark )",
|
|
135
|
+
hover: "var( --kendo-color-dark-hover )",
|
|
136
|
+
active: "var( --kendo-color-dark-active )",
|
|
137
|
+
emphasis: "var( --kendo-color-dark-emphasis )",
|
|
138
|
+
subtle: "var( --kendo-color-dark-subtle )",
|
|
139
|
+
"subtle-hover": "var( --kendo-color-dark-subtle-hover )",
|
|
140
|
+
"subtle-active": "var( --kendo-color-dark-subtle-active)",
|
|
141
|
+
"on-subtle": "var( --kendo-color-dark-on-subtle )",
|
|
142
|
+
"on-surface": "var( --kendo-color-dark-on-surface )",
|
|
143
|
+
},
|
|
144
|
+
"on-dark": "var( --kendo-color-on-dark )",
|
|
145
|
+
info: {
|
|
146
|
+
DEFAULT: "var( --kendo-color-info )",
|
|
147
|
+
hover: "var( --kendo-color-info-hover )",
|
|
148
|
+
active: "var( --kendo-color-info-active )",
|
|
149
|
+
emphasis: "var( --kendo-color-info-emphasis )",
|
|
150
|
+
subtle: "var( --kendo-color-info-subtle )",
|
|
151
|
+
"subtle-hover": "var( --kendo-color-info-subtle-hover )",
|
|
152
|
+
"subtle-active": "var( --kendo-color-info-subtle-active)",
|
|
153
|
+
"on-subtle": "var( --kendo-color-info-on-subtle )",
|
|
154
|
+
"on-surface": "var( --kendo-color-info-on-surface )",
|
|
155
|
+
},
|
|
156
|
+
"on-info": "var( --kendo-color-on-info )",
|
|
157
|
+
success: {
|
|
158
|
+
DEFAULT: "var( --kendo-color-success )",
|
|
159
|
+
hover: "var( --kendo-color-success-hover )",
|
|
160
|
+
active: "var( --kendo-color-success-active )",
|
|
161
|
+
emphasis: "var( --kendo-color-success-emphasis )",
|
|
162
|
+
subtle: "var( --kendo-color-success-subtle )",
|
|
163
|
+
"subtle-hover": "var( --kendo-color-success-subtle-hover )",
|
|
164
|
+
"subtle-active":
|
|
165
|
+
"var( --kendo-color-success-subtle-active)",
|
|
166
|
+
"on-subtle": "var( --kendo-color-success-on-subtle )",
|
|
167
|
+
"on-surface": "var( --kendo-color-success-on-surface )",
|
|
168
|
+
},
|
|
169
|
+
"on-success": "var( --kendo-color-on-success )",
|
|
170
|
+
error: {
|
|
171
|
+
DEFAULT: "var( --kendo-color-error )",
|
|
172
|
+
hover: "var( --kendo-color-error-hover )",
|
|
173
|
+
active: "var( --kendo-color-error-active )",
|
|
174
|
+
emphasis: "var( --kendo-color-error-emphasis )",
|
|
175
|
+
subtle: "var( --kendo-color-error-subtle )",
|
|
176
|
+
"subtle-hover": "var( --kendo-color-error-subtle-hover )",
|
|
177
|
+
"subtle-active": "var( --kendo-color-error-subtle-active)",
|
|
178
|
+
"on-subtle": "var( --kendo-color-error-on-subtle )",
|
|
179
|
+
"on-surface": "var( --kendo-color-error-on-surface )",
|
|
180
|
+
},
|
|
181
|
+
"on-error": "var( --kendo-color-on-error )",
|
|
182
|
+
warning: {
|
|
183
|
+
DEFAULT: "var( --kendo-color-warning )",
|
|
184
|
+
hover: "var( --kendo-color-warning-hover )",
|
|
185
|
+
active: "var( --kendo-color-warning-active )",
|
|
186
|
+
emphasis: "var( --kendo-color-warning-emphasis )",
|
|
187
|
+
subtle: "var( --kendo-color-warning-subtle )",
|
|
188
|
+
"subtle-hover": "var( --kendo-color-warning-subtle-hover )",
|
|
189
|
+
"subtle-active":
|
|
190
|
+
"var( --kendo-color-warning-subtle-active)",
|
|
191
|
+
"on-subtle": "var( --kendo-color-warning-on-subtle )",
|
|
192
|
+
"on-surface": "var( --kendo-color-warning-on-surface )",
|
|
193
|
+
},
|
|
194
|
+
"on-warning": "var( --kendo-color-on-warning )",
|
|
195
|
+
"series-a": {
|
|
196
|
+
DEFAULT: "var( --kendo-color-series-a )",
|
|
197
|
+
subtle: "var( --kendo-color-series-a-subtle )",
|
|
198
|
+
subtler: "var( --kendo-color-series-a-subtler )",
|
|
199
|
+
bold: "var( --kendo-color-series-a-bold )",
|
|
200
|
+
bolder: "var( --kendo-color-series-a-bolder )",
|
|
201
|
+
},
|
|
202
|
+
"series-b": {
|
|
203
|
+
DEFAULT: "var( --kendo-color-series-b )",
|
|
204
|
+
subtle: "var( --kendo-color-series-b-subtle )",
|
|
205
|
+
subtler: "var( --kendo-color-series-b-subtler )",
|
|
206
|
+
bold: "var( --kendo-color-series-b-bold )",
|
|
207
|
+
bolder: "var( --kendo-color-series-b-bolder )",
|
|
208
|
+
},
|
|
209
|
+
"series-c": {
|
|
210
|
+
DEFAULT: "var( --kendo-color-series-c )",
|
|
211
|
+
subtle: "var( --kendo-color-series-c-subtle )",
|
|
212
|
+
subtler: "var( --kendo-color-series-c-subtler )",
|
|
213
|
+
bold: "var( --kendo-color-series-c-bold )",
|
|
214
|
+
bolder: "var( --kendo-color-series-c-bolder )",
|
|
215
|
+
},
|
|
216
|
+
"series-d": {
|
|
217
|
+
DEFAULT: "var( --kendo-color-series-d )",
|
|
218
|
+
subtle: "var( --kendo-color-series-d-subtle )",
|
|
219
|
+
subtler: "var( --kendo-color-series-d-subtler )",
|
|
220
|
+
bold: "var( --kendo-color-series-d-bold )",
|
|
221
|
+
bolder: "var( --kendo-color-series-d-bolder )",
|
|
222
|
+
},
|
|
223
|
+
"series-e": {
|
|
224
|
+
DEFAULT: "var( --kendo-color-series-e )",
|
|
225
|
+
subtle: "var( --kendo-color-series-e-subtle )",
|
|
226
|
+
subtler: "var( --kendo-color-series-e-subtler )",
|
|
227
|
+
bold: "var( --kendo-color-series-e-bold )",
|
|
228
|
+
bolder: "var( --kendo-color-series-e-bolder )",
|
|
229
|
+
},
|
|
230
|
+
"series-f": {
|
|
231
|
+
DEFAULT: "var( --kendo-color-series-f )",
|
|
232
|
+
subtle: "var( --kendo-color-series-f-subtle )",
|
|
233
|
+
subtler: "var( --kendo-color-series-f-subtler )",
|
|
234
|
+
bold: "var( --kendo-color-series-f-bold )",
|
|
235
|
+
bolder: "var( --kendo-color-series-f-bolder )",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|