create-ec-app 0.0.1
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 +204 -0
- package/bin/index.js +2 -0
- package/dist/creators/mobile.d.ts +2 -0
- package/dist/creators/mobile.d.ts.map +1 -0
- package/dist/creators/mobile.js +220 -0
- package/dist/creators/mobile.js.map +1 -0
- package/dist/creators/portal.d.ts +2 -0
- package/dist/creators/portal.d.ts.map +1 -0
- package/dist/creators/portal.js +885 -0
- package/dist/creators/portal.js.map +1 -0
- package/dist/creators/powerpages.d.ts +2 -0
- package/dist/creators/powerpages.d.ts.map +1 -0
- package/dist/creators/powerpages.js +475 -0
- package/dist/creators/powerpages.js.map +1 -0
- package/dist/creators/webresource.d.ts +2 -0
- package/dist/creators/webresource.d.ts.map +1 -0
- package/dist/creators/webresource.js +505 -0
- package/dist/creators/webresource.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
// Helper function to run commands and show a spinner
|
|
8
|
+
const runCommand = (command, spinnerMessage) => {
|
|
9
|
+
const spinner = ora(spinnerMessage).start();
|
|
10
|
+
try {
|
|
11
|
+
execSync(command, { stdio: "pipe" });
|
|
12
|
+
spinner.succeed();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
spinner.fail();
|
|
16
|
+
console.error(chalk.red(`Failed to execute command: ${command}`));
|
|
17
|
+
console.error(error);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
// Template functions
|
|
22
|
+
const getKendoTailwindPreset = () => `module.exports = {
|
|
23
|
+
theme: {
|
|
24
|
+
extend: {
|
|
25
|
+
spacing: {
|
|
26
|
+
1: "var( --kendo-spacing-1 )",
|
|
27
|
+
1.5: "var( --kendo-spacing-1.5 )",
|
|
28
|
+
2: "var( --kendo-spacing-2 )",
|
|
29
|
+
2.5: "var( --kendo-spacing-2.5 )",
|
|
30
|
+
3: "var( --kendo-spacing-3 )",
|
|
31
|
+
3.5: "var( --kendo-spacing-3.5 )",
|
|
32
|
+
4: "var( --kendo-spacing-4 )",
|
|
33
|
+
4.5: "var( --kendo-spacing-4.5 )",
|
|
34
|
+
5: "var( --kendo-spacing-5 )",
|
|
35
|
+
5.5: "var( --kendo-spacing-5.5 )",
|
|
36
|
+
6: "var( --kendo-spacing-6 )",
|
|
37
|
+
6.5: "var( --kendo-spacing-6.5 )",
|
|
38
|
+
7: "var( --kendo-spacing-7 )",
|
|
39
|
+
7.5: "var( --kendo-spacing-7.5 )",
|
|
40
|
+
8: "var( --kendo-spacing-8 )",
|
|
41
|
+
9: "var( --kendo-spacing-9 )",
|
|
42
|
+
10: "var( --kendo-spacing-10 )",
|
|
43
|
+
11: "var( --kendo-spacing-11 )",
|
|
44
|
+
12: "var( --kendo-spacing-12 )",
|
|
45
|
+
13: "var( --kendo-spacing-13 )",
|
|
46
|
+
14: "var( --kendo-spacing-14 )",
|
|
47
|
+
15: "var( --kendo-spacing-15 )",
|
|
48
|
+
16: "var( --kendo-spacing-16 )",
|
|
49
|
+
17: "var( --kendo-spacing-17 )",
|
|
50
|
+
18: "var( --kendo-spacing-18 )",
|
|
51
|
+
19: "var( --kendo-spacing-19 )",
|
|
52
|
+
20: "var( --kendo-spacing-20 )",
|
|
53
|
+
21: "var( --kendo-spacing-21 )",
|
|
54
|
+
22: "var( --kendo-spacing-22 )",
|
|
55
|
+
23: "var( --kendo-spacing-23 )",
|
|
56
|
+
24: "var( --kendo-spacing-24 )",
|
|
57
|
+
25: "var( --kendo-spacing-25 )",
|
|
58
|
+
26: "var( --kendo-spacing-26 )",
|
|
59
|
+
27: "var( --kendo-spacing-27 )",
|
|
60
|
+
28: "var( --kendo-spacing-28 )",
|
|
61
|
+
29: "var( --kendo-spacing-29 )",
|
|
62
|
+
30: "var( --kendo-spacing-30 )",
|
|
63
|
+
},
|
|
64
|
+
borderRadius: {
|
|
65
|
+
none: "var( --kendo-border-radius-none )",
|
|
66
|
+
sm: "var( --kendo-border-radius-sm )",
|
|
67
|
+
DEFAULT: "var( --kendo-border-radius-md )",
|
|
68
|
+
lg: "var( --kendo-border-radius-lg )",
|
|
69
|
+
xl: "var( --kendo-border-radius-xl )",
|
|
70
|
+
"2xl": "var( --kendo-border-radius-xxl )",
|
|
71
|
+
"3xl": "var( --kendo-border-radius-xxxl )",
|
|
72
|
+
full: "var( --kendo-border-radius-none )",
|
|
73
|
+
},
|
|
74
|
+
boxShadow: {
|
|
75
|
+
sm: "var( --kendo-elevation-2 )",
|
|
76
|
+
DEFAULT: "var( --kendo-elevation-4 )",
|
|
77
|
+
lg: "var( --kendo-elevation-6 )",
|
|
78
|
+
xl: "var( --kendo-elevation-8 )",
|
|
79
|
+
"2xl": "var( --kendo-elevation-9 )",
|
|
80
|
+
},
|
|
81
|
+
colors: {
|
|
82
|
+
"app-surface": "var( --kendo-color-app-surface )",
|
|
83
|
+
"on-app-surface": "var( --kendo-color-on-app-surface )",
|
|
84
|
+
subtle: "var( --kendo-color-subtle )",
|
|
85
|
+
surface: "var( --kendo-color-surface )",
|
|
86
|
+
"surface-alt": "var( --kendo-color-surface-alt )",
|
|
87
|
+
border: "var( --kendo-color-border )",
|
|
88
|
+
"border-alt": "var( --kendo-color-border-alt )",
|
|
89
|
+
base: {
|
|
90
|
+
DEFAULT: "var( --kendo-color-base )",
|
|
91
|
+
hover: "var( --kendo-color-base-hover )",
|
|
92
|
+
active: "var( --kendo-color-base-active )",
|
|
93
|
+
emphasis: "var( --kendo-color-base-emphasis )",
|
|
94
|
+
subtle: "var( --kendo-color-base-subtle )",
|
|
95
|
+
"subtle-hover": "var( --kendo-color-base-subtle-hover )",
|
|
96
|
+
"subtle-active": "var( --kendo-color-base-subtle-active)",
|
|
97
|
+
"on-subtle": "var( --kendo-color-base-on-subtle )",
|
|
98
|
+
"on-surface": "var( --kendo-color-base-on-surface )",
|
|
99
|
+
},
|
|
100
|
+
"on-base": "var( --kendo-color-on-base )",
|
|
101
|
+
primary: {
|
|
102
|
+
DEFAULT: "var( --kendo-color-primary )",
|
|
103
|
+
hover: "var( --kendo-color-primary-hover )",
|
|
104
|
+
active: "var( --kendo-color-primary-active )",
|
|
105
|
+
emphasis: "var( --kendo-color-primary-emphasis )",
|
|
106
|
+
subtle: "var( --kendo-color-primary-subtle )",
|
|
107
|
+
"subtle-hover": "var( --kendo-color-primary-subtle-hover )",
|
|
108
|
+
"subtle-active": "var( --kendo-color-primary-subtle-active)",
|
|
109
|
+
"on-subtle": "var( --kendo-color-primary-on-subtle )",
|
|
110
|
+
"on-surface": "var( --kendo-color-primary-on-surface )",
|
|
111
|
+
},
|
|
112
|
+
"on-primary": "var( --kendo-color-on-primary )",
|
|
113
|
+
secondary: {
|
|
114
|
+
DEFAULT: "var( --kendo-color-secondary )",
|
|
115
|
+
hover: "var( --kendo-color-secondary-hover )",
|
|
116
|
+
active: "var( --kendo-color-secondary-active )",
|
|
117
|
+
emphasis: "var( --kendo-color-secondary-emphasis )",
|
|
118
|
+
subtle: "var( --kendo-color-secondary-subtle )",
|
|
119
|
+
"subtle-hover": "var( --kendo-color-secondary-subtle-hover )",
|
|
120
|
+
"subtle-active": "var( --kendo-color-secondary-subtle-active)",
|
|
121
|
+
"on-subtle": "var( --kendo-color-secondary-on-subtle )",
|
|
122
|
+
"on-surface": "var( --kendo-color-secondary-on-surface )",
|
|
123
|
+
},
|
|
124
|
+
"on-secondary": "var( --kendo-color-on-secondary )",
|
|
125
|
+
tertiary: {
|
|
126
|
+
DEFAULT: "var( --kendo-color-tertiary )",
|
|
127
|
+
hover: "var( --kendo-color-tertiary-hover )",
|
|
128
|
+
active: "var( --kendo-color-tertiary-active )",
|
|
129
|
+
emphasis: "var( --kendo-color-tertiary-emphasis )",
|
|
130
|
+
subtle: "var( --kendo-color-tertiary-subtle )",
|
|
131
|
+
"subtle-hover": "var( --kendo-color-tertiary-subtle-hover )",
|
|
132
|
+
"subtle-active": "var( --kendo-color-tertiary-subtle-active)",
|
|
133
|
+
"on-subtle": "var( --kendo-color-tertiary-on-subtle )",
|
|
134
|
+
"on-surface": "var( --kendo-color-tertiary-on-surface )",
|
|
135
|
+
},
|
|
136
|
+
"on-tertiary": "var( --kendo-color-on-tertiary )",
|
|
137
|
+
info: {
|
|
138
|
+
DEFAULT: "var( --kendo-color-info )",
|
|
139
|
+
hover: "var( --kendo-color-info-hover )",
|
|
140
|
+
active: "var( --kendo-color-info-active )",
|
|
141
|
+
emphasis: "var( --kendo-color-info-emphasis )",
|
|
142
|
+
subtle: "var( --kendo-color-info-subtle )",
|
|
143
|
+
"subtle-hover": "var( --kendo-color-info-subtle-hover )",
|
|
144
|
+
"subtle-active": "var( --kendo-color-info-subtle-active)",
|
|
145
|
+
"on-subtle": "var( --kendo-color-info-on-subtle )",
|
|
146
|
+
"on-surface": "var( --kendo-color-info-on-surface )",
|
|
147
|
+
},
|
|
148
|
+
"on-info": "var( --kendo-color-on-info )",
|
|
149
|
+
success: {
|
|
150
|
+
DEFAULT: "var( --kendo-color-success )",
|
|
151
|
+
hover: "var( --kendo-color-success-hover )",
|
|
152
|
+
active: "var( --kendo-color-success-active )",
|
|
153
|
+
emphasis: "var( --kendo-color-success-emphasis )",
|
|
154
|
+
subtle: "var( --kendo-color-success-subtle )",
|
|
155
|
+
"subtle-hover": "var( --kendo-color-success-subtle-hover )",
|
|
156
|
+
"subtle-active": "var( --kendo-color-success-subtle-active)",
|
|
157
|
+
"on-subtle": "var( --kendo-color-success-on-subtle )",
|
|
158
|
+
"on-surface": "var( --kendo-color-success-on-surface )",
|
|
159
|
+
},
|
|
160
|
+
"on-success": "var( --kendo-color-on-success )",
|
|
161
|
+
error: {
|
|
162
|
+
DEFAULT: "var( --kendo-color-error )",
|
|
163
|
+
hover: "var( --kendo-color-error-hover )",
|
|
164
|
+
active: "var( --kendo-color-error-active )",
|
|
165
|
+
emphasis: "var( --kendo-color-error-emphasis )",
|
|
166
|
+
subtle: "var( --kendo-color-error-subtle )",
|
|
167
|
+
"subtle-hover": "var( --kendo-color-error-subtle-hover )",
|
|
168
|
+
"subtle-active": "var( --kendo-color-error-subtle-active)",
|
|
169
|
+
"on-subtle": "var( --kendo-color-error-on-subtle )",
|
|
170
|
+
"on-surface": "var( --kendo-color-error-on-surface )",
|
|
171
|
+
},
|
|
172
|
+
"on-error": "var( --kendo-color-on-error )",
|
|
173
|
+
warning: {
|
|
174
|
+
DEFAULT: "var( --kendo-color-warning )",
|
|
175
|
+
hover: "var( --kendo-color-warning-hover )",
|
|
176
|
+
active: "var( --kendo-color-warning-active )",
|
|
177
|
+
emphasis: "var( --kendo-color-warning-emphasis )",
|
|
178
|
+
subtle: "var( --kendo-color-warning-subtle )",
|
|
179
|
+
"subtle-hover": "var( --kendo-color-warning-subtle-hover )",
|
|
180
|
+
"subtle-active": "var( --kendo-color-warning-subtle-active)",
|
|
181
|
+
"on-subtle": "var( --kendo-color-warning-on-subtle )",
|
|
182
|
+
"on-surface": "var( --kendo-color-warning-on-surface )",
|
|
183
|
+
},
|
|
184
|
+
"on-warning": "var( --kendo-color-on-warning )",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};`;
|
|
189
|
+
const getTailwindConfig = () => `/** @type {import('tailwindcss').Config} */
|
|
190
|
+
const kendoTwPreset = require('./kendo-tw-preset.js');
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
content: [
|
|
194
|
+
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
195
|
+
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
196
|
+
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
197
|
+
],
|
|
198
|
+
presets: [kendoTwPreset],
|
|
199
|
+
theme: {
|
|
200
|
+
extend: {},
|
|
201
|
+
},
|
|
202
|
+
plugins: [],
|
|
203
|
+
}`;
|
|
204
|
+
const getLayoutTemplate = (kendoThemePackage) => `import { Inter } from 'next/font/google'
|
|
205
|
+
import '@progress/kendo-theme-${kendoThemePackage.split("-").pop()}/dist/all.css'
|
|
206
|
+
import './globals.css'
|
|
207
|
+
import { Providers } from './providers'
|
|
208
|
+
|
|
209
|
+
const inter = Inter({ subsets: ['latin'] })
|
|
210
|
+
|
|
211
|
+
export const metadata = {
|
|
212
|
+
title: 'Portal App',
|
|
213
|
+
description: 'Next.js Portal application with Kendo UI',
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default function RootLayout({
|
|
217
|
+
children,
|
|
218
|
+
}: {
|
|
219
|
+
children: React.ReactNode
|
|
220
|
+
}) {
|
|
221
|
+
return (
|
|
222
|
+
<html lang="en">
|
|
223
|
+
<body className={inter.className}>
|
|
224
|
+
<Providers>
|
|
225
|
+
{children}
|
|
226
|
+
</Providers>
|
|
227
|
+
</body>
|
|
228
|
+
</html>
|
|
229
|
+
)
|
|
230
|
+
}`;
|
|
231
|
+
const getShadcnLayoutTemplate = () => `import { Inter } from 'next/font/google'
|
|
232
|
+
import './globals.css'
|
|
233
|
+
import { Providers } from './providers'
|
|
234
|
+
|
|
235
|
+
const inter = Inter({ subsets: ['latin'] })
|
|
236
|
+
|
|
237
|
+
export const metadata = {
|
|
238
|
+
title: 'Portal App',
|
|
239
|
+
description: 'Next.js Portal application with Shadcn/ui',
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export default function RootLayout({
|
|
243
|
+
children,
|
|
244
|
+
}: {
|
|
245
|
+
children: React.ReactNode
|
|
246
|
+
}) {
|
|
247
|
+
return (
|
|
248
|
+
<html lang="en">
|
|
249
|
+
<body className={inter.className}>
|
|
250
|
+
<Providers>
|
|
251
|
+
{children}
|
|
252
|
+
</Providers>
|
|
253
|
+
</body>
|
|
254
|
+
</html>
|
|
255
|
+
)
|
|
256
|
+
}`;
|
|
257
|
+
const getProvidersTemplate = () => `'use client'
|
|
258
|
+
|
|
259
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
260
|
+
import { SessionProvider } from 'next-auth/react'
|
|
261
|
+
import { useState } from 'react'
|
|
262
|
+
|
|
263
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
264
|
+
const [queryClient] = useState(() => new QueryClient({
|
|
265
|
+
defaultOptions: {
|
|
266
|
+
queries: {
|
|
267
|
+
refetchOnWindowFocus: false,
|
|
268
|
+
retry: 3,
|
|
269
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
270
|
+
},
|
|
271
|
+
mutations: {
|
|
272
|
+
retry: 1,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
}))
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<SessionProvider>
|
|
279
|
+
<QueryClientProvider client={queryClient}>
|
|
280
|
+
{children}
|
|
281
|
+
</QueryClientProvider>
|
|
282
|
+
</SessionProvider>
|
|
283
|
+
)
|
|
284
|
+
}`;
|
|
285
|
+
const getShadcnProvidersTemplate = () => `'use client'
|
|
286
|
+
|
|
287
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
288
|
+
import { SessionProvider } from 'next-auth/react'
|
|
289
|
+
import { useState } from 'react'
|
|
290
|
+
|
|
291
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
292
|
+
const [queryClient] = useState(() => new QueryClient({
|
|
293
|
+
defaultOptions: {
|
|
294
|
+
queries: {
|
|
295
|
+
refetchOnWindowFocus: false,
|
|
296
|
+
retry: 3,
|
|
297
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
298
|
+
},
|
|
299
|
+
mutations: {
|
|
300
|
+
retry: 1,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
}))
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<SessionProvider>
|
|
307
|
+
<QueryClientProvider client={queryClient}>
|
|
308
|
+
{children}
|
|
309
|
+
</QueryClientProvider>
|
|
310
|
+
</SessionProvider>
|
|
311
|
+
)
|
|
312
|
+
}`;
|
|
313
|
+
const getPageTemplate = () => `'use client';
|
|
314
|
+
|
|
315
|
+
import { Button } from '@progress/kendo-react-buttons';
|
|
316
|
+
import { signIn, signOut, useSession } from 'next-auth/react';
|
|
317
|
+
import { useDynamicsAccounts } from '@/hooks/useDynamicsAccounts';
|
|
318
|
+
|
|
319
|
+
export default function Home() {
|
|
320
|
+
const { data: session, status } = useSession();
|
|
321
|
+
const { data: accounts, isLoading, error } = useDynamicsAccounts();
|
|
322
|
+
|
|
323
|
+
if (status === 'loading') {
|
|
324
|
+
return (
|
|
325
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
326
|
+
<div className="text-center">
|
|
327
|
+
<h1 className="text-4xl font-bold mb-8">Loading...</h1>
|
|
328
|
+
</div>
|
|
329
|
+
</main>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!session) {
|
|
334
|
+
return (
|
|
335
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
336
|
+
<div className="text-center">
|
|
337
|
+
<h1 className="text-4xl font-bold mb-8">Welcome to Portal App</h1>
|
|
338
|
+
<p className="text-lg mb-8">Sign in to access your Dynamics 365 data</p>
|
|
339
|
+
<Button onClick={() => signIn('azure-ad')}>
|
|
340
|
+
Sign in with Microsoft
|
|
341
|
+
</Button>
|
|
342
|
+
</div>
|
|
343
|
+
</main>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<main className="flex min-h-screen flex-col p-24">
|
|
349
|
+
<div className="mb-8">
|
|
350
|
+
<h1 className="text-4xl font-bold mb-4">Welcome, {session.user?.name}!</h1>
|
|
351
|
+
<Button onClick={() => signOut()}>Sign out</Button>
|
|
352
|
+
</div>
|
|
353
|
+
|
|
354
|
+
<div className="mb-8">
|
|
355
|
+
<h2 className="text-2xl font-bold mb-4">Dynamics 365 Accounts</h2>
|
|
356
|
+
|
|
357
|
+
{isLoading && <p>Loading accounts...</p>}
|
|
358
|
+
{error && <p className="text-red-500">Error loading accounts: {error.message}</p>}
|
|
359
|
+
|
|
360
|
+
{accounts && (
|
|
361
|
+
<div className="grid gap-4">
|
|
362
|
+
{accounts.map((account) => (
|
|
363
|
+
<div key={account.accountid} className="border p-4 rounded">
|
|
364
|
+
<h3 className="font-semibold">{account.name}</h3>
|
|
365
|
+
<p className="text-gray-600">{account.emailaddress1}</p>
|
|
366
|
+
<p className="text-sm text-gray-500">ID: {account.accountid}</p>
|
|
367
|
+
</div>
|
|
368
|
+
))}
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
371
|
+
</div>
|
|
372
|
+
</main>
|
|
373
|
+
);
|
|
374
|
+
}`;
|
|
375
|
+
const getShadcnPageTemplate = () => `'use client';
|
|
376
|
+
|
|
377
|
+
import { Button } from '@/components/ui/button';
|
|
378
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
379
|
+
import { signIn, signOut, useSession } from 'next-auth/react';
|
|
380
|
+
import { useDynamicsAccounts } from '@/hooks/useDynamicsAccounts';
|
|
381
|
+
|
|
382
|
+
export default function Home() {
|
|
383
|
+
const { data: session, status } = useSession();
|
|
384
|
+
const { data: accounts, isLoading, error } = useDynamicsAccounts();
|
|
385
|
+
|
|
386
|
+
if (status === 'loading') {
|
|
387
|
+
return (
|
|
388
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
389
|
+
<div className="text-center">
|
|
390
|
+
<h1 className="text-4xl font-bold mb-8">Loading...</h1>
|
|
391
|
+
</div>
|
|
392
|
+
</main>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!session) {
|
|
397
|
+
return (
|
|
398
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
399
|
+
<div className="text-center">
|
|
400
|
+
<h1 className="text-4xl font-bold mb-8">Welcome to Portal App</h1>
|
|
401
|
+
<p className="text-lg mb-8">Sign in to access your Dynamics 365 data</p>
|
|
402
|
+
<Button onClick={() => signIn('azure-ad')}>
|
|
403
|
+
Sign in with Microsoft
|
|
404
|
+
</Button>
|
|
405
|
+
</div>
|
|
406
|
+
</main>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<main className="flex min-h-screen flex-col p-24">
|
|
412
|
+
<div className="mb-8">
|
|
413
|
+
<h1 className="text-4xl font-bold mb-4">Welcome, {session.user?.name}!</h1>
|
|
414
|
+
<Button onClick={() => signOut()}>Sign out</Button>
|
|
415
|
+
</div>
|
|
416
|
+
|
|
417
|
+
<div className="mb-8">
|
|
418
|
+
<h2 className="text-2xl font-bold mb-4">Dynamics 365 Accounts</h2>
|
|
419
|
+
|
|
420
|
+
{isLoading && <p>Loading accounts...</p>}
|
|
421
|
+
{error && <p className="text-red-500">Error loading accounts: {error.message}</p>}
|
|
422
|
+
|
|
423
|
+
{accounts && (
|
|
424
|
+
<div className="grid gap-4">
|
|
425
|
+
{accounts.map((account) => (
|
|
426
|
+
<Card key={account.accountid}>
|
|
427
|
+
<CardHeader>
|
|
428
|
+
<CardTitle>{account.name}</CardTitle>
|
|
429
|
+
<CardDescription>{account.emailaddress1}</CardDescription>
|
|
430
|
+
</CardHeader>
|
|
431
|
+
<CardContent>
|
|
432
|
+
<p className="text-sm text-gray-500">ID: {account.accountid}</p>
|
|
433
|
+
</CardContent>
|
|
434
|
+
</Card>
|
|
435
|
+
))}
|
|
436
|
+
</div>
|
|
437
|
+
)}
|
|
438
|
+
</div>
|
|
439
|
+
</main>
|
|
440
|
+
);
|
|
441
|
+
}`;
|
|
442
|
+
const getGlobalsCSS = () => `@tailwind base;
|
|
443
|
+
@tailwind components;
|
|
444
|
+
@tailwind utilities;
|
|
445
|
+
|
|
446
|
+
:root {
|
|
447
|
+
--foreground-rgb: 0, 0, 0;
|
|
448
|
+
--background-start-rgb: 214, 219, 220;
|
|
449
|
+
--background-end-rgb: 255, 255, 255;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@media (prefers-color-scheme: dark) {
|
|
453
|
+
:root {
|
|
454
|
+
--foreground-rgb: 255, 255, 255;
|
|
455
|
+
--background-start-rgb: 0, 0, 0;
|
|
456
|
+
--background-end-rgb: 0, 0, 0;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
body {
|
|
461
|
+
color: rgb(var(--foreground-rgb));
|
|
462
|
+
background: linear-gradient(
|
|
463
|
+
to bottom,
|
|
464
|
+
transparent,
|
|
465
|
+
rgb(var(--background-end-rgb))
|
|
466
|
+
)
|
|
467
|
+
rgb(var(--background-start-rgb));
|
|
468
|
+
}`;
|
|
469
|
+
const getPrettierConfig = () => `{
|
|
470
|
+
"tabWidth": 2,
|
|
471
|
+
"useTabs": false,
|
|
472
|
+
"semi": true,
|
|
473
|
+
"singleQuote": true,
|
|
474
|
+
"trailingComma": "es5",
|
|
475
|
+
"bracketSpacing": true,
|
|
476
|
+
"jsxBracketSameLine": false,
|
|
477
|
+
"arrowParens": "always",
|
|
478
|
+
"printWidth": 80
|
|
479
|
+
}`;
|
|
480
|
+
const getEnvConfig = () => `# NextAuth.js Configuration
|
|
481
|
+
NEXTAUTH_URL=http://localhost:3000
|
|
482
|
+
|
|
483
|
+
# Microsoft Azure AD Configuration
|
|
484
|
+
AZURE_AD_CLIENT_ID=your-azure-ad-client-id
|
|
485
|
+
AZURE_AD_CLIENT_SECRET=your-azure-ad-client-secret
|
|
486
|
+
AZURE_AD_TENANT_ID=your-azure-ad-tenant-id
|
|
487
|
+
|
|
488
|
+
# Dynamics 365 Configuration
|
|
489
|
+
DYNAMICS_BASE_URL=https://your-org.crm.dynamics.com
|
|
490
|
+
DYNAMICS_API_VERSION=v9.2
|
|
491
|
+
`;
|
|
492
|
+
const authConfig = () => `import NextAuth from 'next-auth'
|
|
493
|
+
import AzureADProvider from 'next-auth/providers/azure-ad'
|
|
494
|
+
|
|
495
|
+
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
496
|
+
providers: [
|
|
497
|
+
AzureADProvider({
|
|
498
|
+
clientId: process.env.AZURE_AD_CLIENT_ID!,
|
|
499
|
+
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
|
|
500
|
+
tenantId: process.env.AZURE_AD_TENANT_ID!,
|
|
501
|
+
}),
|
|
502
|
+
],
|
|
503
|
+
callbacks: {
|
|
504
|
+
async jwt({ token, account }) {
|
|
505
|
+
if (account) {
|
|
506
|
+
token.accessToken = account.access_token
|
|
507
|
+
}
|
|
508
|
+
return token
|
|
509
|
+
},
|
|
510
|
+
async session({ session, token }) {
|
|
511
|
+
session.accessToken = token.accessToken as string
|
|
512
|
+
return session
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
pages: {
|
|
516
|
+
signIn: '/auth/signin',
|
|
517
|
+
error: '/auth/error',
|
|
518
|
+
},
|
|
519
|
+
})`;
|
|
520
|
+
const getAuthRoute = () => `import { handlers } from '@/auth'
|
|
521
|
+
|
|
522
|
+
export const { GET, POST } = handlers`;
|
|
523
|
+
const getDynamicsAccountsRoute = () => `import { auth } from '@/auth'
|
|
524
|
+
import { getDynamicsData } from '@/lib/dynamics'
|
|
525
|
+
import { NextResponse } from 'next/server'
|
|
526
|
+
|
|
527
|
+
export async function GET() {
|
|
528
|
+
try {
|
|
529
|
+
const session = await auth()
|
|
530
|
+
|
|
531
|
+
if (!session) {
|
|
532
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const accounts = await getDynamicsData('accounts', {
|
|
536
|
+
select: ['accountid', 'name', 'emailaddress1', 'telephone1', 'websiteurl'],
|
|
537
|
+
top: 10,
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
return NextResponse.json(accounts)
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.error('Error fetching accounts:', error)
|
|
543
|
+
return NextResponse.json(
|
|
544
|
+
{ error: 'Failed to fetch accounts' },
|
|
545
|
+
{ status: 500 }
|
|
546
|
+
)
|
|
547
|
+
}
|
|
548
|
+
}`;
|
|
549
|
+
const getDynamicsLib = () => `export interface DynamicsAccount {
|
|
550
|
+
accountid: string
|
|
551
|
+
name: string
|
|
552
|
+
emailaddress1?: string
|
|
553
|
+
telephone1?: string
|
|
554
|
+
websiteurl?: string
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
interface QueryOptions {
|
|
558
|
+
select?: string[]
|
|
559
|
+
filter?: string
|
|
560
|
+
orderby?: string
|
|
561
|
+
top?: number
|
|
562
|
+
skip?: number
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export async function getDynamicsData(
|
|
566
|
+
entityName: string,
|
|
567
|
+
options: QueryOptions = {}
|
|
568
|
+
): Promise<any> {
|
|
569
|
+
const baseUrl = process.env.DYNAMICS_BASE_URL
|
|
570
|
+
const apiVersion = process.env.DYNAMICS_API_VERSION || 'v9.2'
|
|
571
|
+
|
|
572
|
+
if (!baseUrl) {
|
|
573
|
+
throw new Error('DYNAMICS_BASE_URL environment variable is not set')
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
let url = \`\${baseUrl}/api/data/\${apiVersion}/\${entityName}\`
|
|
577
|
+
const params = new URLSearchParams()
|
|
578
|
+
|
|
579
|
+
if (options.select) {
|
|
580
|
+
params.append('$select', options.select.join(','))
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (options.filter) {
|
|
584
|
+
params.append('$filter', options.filter)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (options.orderby) {
|
|
588
|
+
params.append('$orderby', options.orderby)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (options.top) {
|
|
592
|
+
params.append('$top', options.top.toString())
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (options.skip) {
|
|
596
|
+
params.append('$skip', options.skip.toString())
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const queryString = params.toString()
|
|
600
|
+
if (queryString) {
|
|
601
|
+
url += \`?\${queryString}\`
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const response = await fetch(url, {
|
|
605
|
+
headers: {
|
|
606
|
+
'Accept': 'application/json',
|
|
607
|
+
'OData-MaxVersion': '4.0',
|
|
608
|
+
'OData-Version': '4.0',
|
|
609
|
+
'Prefer': 'odata.include-annotations="*"',
|
|
610
|
+
},
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
if (!response.ok) {
|
|
614
|
+
throw new Error(\`HTTP error! status: \${response.status}\`)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const data = await response.json()
|
|
618
|
+
return data.value || data
|
|
619
|
+
}`;
|
|
620
|
+
const getDynamicsAccountsHook = () => `'use client'
|
|
621
|
+
|
|
622
|
+
import { useQuery } from '@tanstack/react-query'
|
|
623
|
+
import { DynamicsAccount } from '@/lib/dynamics'
|
|
624
|
+
|
|
625
|
+
export function useDynamicsAccounts() {
|
|
626
|
+
return useQuery<DynamicsAccount[]>({
|
|
627
|
+
queryKey: ['dynamics', 'accounts'],
|
|
628
|
+
queryFn: async () => {
|
|
629
|
+
const response = await fetch('/api/dynamics/accounts')
|
|
630
|
+
|
|
631
|
+
if (!response.ok) {
|
|
632
|
+
throw new Error('Failed to fetch accounts')
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return response.json()
|
|
636
|
+
},
|
|
637
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
638
|
+
retry: 3,
|
|
639
|
+
})
|
|
640
|
+
}`;
|
|
641
|
+
const compilerOptions = () => `{
|
|
642
|
+
"compilerOptions": {
|
|
643
|
+
"lib": ["dom", "dom.iterable", "es6"],
|
|
644
|
+
"allowJs": true,
|
|
645
|
+
"skipLibCheck": true,
|
|
646
|
+
"strict": true,
|
|
647
|
+
"noEmit": true,
|
|
648
|
+
"esModuleInterop": true,
|
|
649
|
+
"module": "esnext",
|
|
650
|
+
"moduleResolution": "bundler",
|
|
651
|
+
"resolveJsonModule": true,
|
|
652
|
+
"isolatedModules": true,
|
|
653
|
+
"jsx": "preserve",
|
|
654
|
+
"incremental": true,
|
|
655
|
+
"plugins": [
|
|
656
|
+
{
|
|
657
|
+
"name": "next"
|
|
658
|
+
}
|
|
659
|
+
],
|
|
660
|
+
"baseUrl": ".",
|
|
661
|
+
"paths": {
|
|
662
|
+
"@/*": ["./src/*"]
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
666
|
+
"exclude": ["node_modules"]
|
|
667
|
+
}`;
|
|
668
|
+
const getGithubWorkflow = () => `name: Deploy to Azure
|
|
669
|
+
|
|
670
|
+
on:
|
|
671
|
+
push:
|
|
672
|
+
branches: [ main ]
|
|
673
|
+
workflow_dispatch:
|
|
674
|
+
|
|
675
|
+
jobs:
|
|
676
|
+
build-and-deploy:
|
|
677
|
+
runs-on: ubuntu-latest
|
|
678
|
+
|
|
679
|
+
steps:
|
|
680
|
+
- uses: actions/checkout@v4
|
|
681
|
+
|
|
682
|
+
- name: Setup Node.js
|
|
683
|
+
uses: actions/setup-node@v4
|
|
684
|
+
with:
|
|
685
|
+
node-version: '18'
|
|
686
|
+
cache: 'npm'
|
|
687
|
+
|
|
688
|
+
- name: Install dependencies
|
|
689
|
+
run: npm ci
|
|
690
|
+
|
|
691
|
+
- name: Build application
|
|
692
|
+
run: npm run build
|
|
693
|
+
env:
|
|
694
|
+
NEXTAUTH_URL: \${{ secrets.NEXTAUTH_URL }}
|
|
695
|
+
NEXTAUTH_SECRET: \${{ secrets.NEXTAUTH_SECRET }}
|
|
696
|
+
AZURE_AD_CLIENT_ID: \${{ secrets.AZURE_AD_CLIENT_ID }}
|
|
697
|
+
AZURE_AD_CLIENT_SECRET: \${{ secrets.AZURE_AD_CLIENT_SECRET }}
|
|
698
|
+
AZURE_AD_TENANT_ID: \${{ secrets.AZURE_AD_TENANT_ID }}
|
|
699
|
+
DYNAMICS_BASE_URL: \${{ secrets.DYNAMICS_BASE_URL }}
|
|
700
|
+
DYNAMICS_API_VERSION: \${{ secrets.DYNAMICS_API_VERSION }}
|
|
701
|
+
|
|
702
|
+
- name: Deploy to Azure Web App
|
|
703
|
+
uses: azure/webapps-deploy@v2
|
|
704
|
+
with:
|
|
705
|
+
app-name: \${{ secrets.AZURE_WEBAPP_NAME }}
|
|
706
|
+
publish-profile: \${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
|
|
707
|
+
package: .`;
|
|
708
|
+
const getNextConfig = () => `import type { NextConfig } from 'next'
|
|
709
|
+
|
|
710
|
+
const nextConfig: NextConfig = {
|
|
711
|
+
output: 'standalone',
|
|
712
|
+
experimental: {
|
|
713
|
+
optimizePackageImports: ['@progress/kendo-react-buttons', '@progress/kendo-react-inputs'],
|
|
714
|
+
},
|
|
715
|
+
webpack: (config) => {
|
|
716
|
+
config.resolve.alias = {
|
|
717
|
+
...config.resolve.alias,
|
|
718
|
+
}
|
|
719
|
+
return config
|
|
720
|
+
},
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
export default nextConfig`;
|
|
724
|
+
export const createPortalApp = async (projectName) => {
|
|
725
|
+
// Prompt for UI library choice
|
|
726
|
+
const { uiLibrary } = await inquirer.prompt([
|
|
727
|
+
{
|
|
728
|
+
type: "list",
|
|
729
|
+
name: "uiLibrary",
|
|
730
|
+
message: "Which UI library would you like to use?",
|
|
731
|
+
choices: [
|
|
732
|
+
{ name: "Kendo UI (React components with themes)", value: "kendo" },
|
|
733
|
+
{ name: "Shadcn/ui (Modern React components)", value: "shadcn" },
|
|
734
|
+
],
|
|
735
|
+
},
|
|
736
|
+
]);
|
|
737
|
+
let kendoThemePackage = "";
|
|
738
|
+
if (uiLibrary === "kendo") {
|
|
739
|
+
const response = await inquirer.prompt([
|
|
740
|
+
{
|
|
741
|
+
type: "list",
|
|
742
|
+
name: "kendoThemePackage",
|
|
743
|
+
message: "Which Kendo UI theme would you like to install?",
|
|
744
|
+
choices: [
|
|
745
|
+
{ name: "Default", value: "@progress/kendo-theme-default" },
|
|
746
|
+
{ name: "Bootstrap (v5)", value: "@progress/kendo-theme-bootstrap" },
|
|
747
|
+
{ name: "Material (v3)", value: "@progress/kendo-theme-material" },
|
|
748
|
+
{ name: "Fluent", value: "@progress/kendo-theme-fluent" },
|
|
749
|
+
{ name: "Classic", value: "@progress/kendo-theme-classic" },
|
|
750
|
+
],
|
|
751
|
+
},
|
|
752
|
+
]);
|
|
753
|
+
kendoThemePackage = response.kendoThemePackage;
|
|
754
|
+
}
|
|
755
|
+
// Ensure the project directory exists
|
|
756
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
757
|
+
await fs.ensureDir(projectDir);
|
|
758
|
+
console.log(`\nScaffolding a new project in ${chalk.green(projectDir)}...\n`);
|
|
759
|
+
// Create Next.js project with TypeScript
|
|
760
|
+
runCommand(`npx create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack`, "Creating Next.js + TypeScript project...");
|
|
761
|
+
process.chdir(projectDir);
|
|
762
|
+
// Install additional dependencies based on UI library choice
|
|
763
|
+
let dependencies = ["@tanstack/react-query", "zustand", "next-auth@beta"];
|
|
764
|
+
let devDependencies = [];
|
|
765
|
+
if (uiLibrary === "kendo") {
|
|
766
|
+
dependencies.push("@progress/kendo-react-buttons", "@progress/kendo-react-layout", "@progress/kendo-react-inputs", "@progress/kendo-react-grid", "@progress/kendo-react-dateinputs", "@progress/kendo-react-dropdowns", "@progress/kendo-react-dialogs", "@progress/kendo-licensing", kendoThemePackage);
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
devDependencies.push("tailwindcss-animate");
|
|
770
|
+
}
|
|
771
|
+
const installMessage = uiLibrary === "kendo"
|
|
772
|
+
? `Installing dependencies (Kendo Theme: ${kendoThemePackage.split("/")[1]})...`
|
|
773
|
+
: "Installing dependencies (Shadcn/ui)...";
|
|
774
|
+
runCommand(`npm install ${dependencies.join(" ")}`, installMessage);
|
|
775
|
+
if (devDependencies.length > 0) {
|
|
776
|
+
runCommand(`npm install -D ${devDependencies.join(" ")}`, "Installing dev dependencies...");
|
|
777
|
+
}
|
|
778
|
+
// Update package.json with Portal-specific scripts
|
|
779
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
780
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
781
|
+
if (!packageJson.scripts) {
|
|
782
|
+
packageJson.scripts = {};
|
|
783
|
+
}
|
|
784
|
+
packageJson.scripts["export"] = "next build";
|
|
785
|
+
packageJson.scripts["serve"] = "npx serve@latest dist";
|
|
786
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
787
|
+
ora("Updated package.json with Portal-specific scripts").succeed();
|
|
788
|
+
if (uiLibrary === "kendo") {
|
|
789
|
+
// Create Kendo Tailwind preset
|
|
790
|
+
const kendoPresetPath = path.join(projectDir, "kendo-tw-preset.js");
|
|
791
|
+
await fs.writeFile(kendoPresetPath, getKendoTailwindPreset(), "utf8");
|
|
792
|
+
ora("Created kendo-tw-preset.js").succeed();
|
|
793
|
+
// Replace Tailwind config
|
|
794
|
+
const tailwindConfigPath = path.join(projectDir, "tailwind.config.js");
|
|
795
|
+
await fs.writeFile(tailwindConfigPath, getTailwindConfig(), "utf8");
|
|
796
|
+
ora("Updated tailwind.config.js with Kendo preset").succeed();
|
|
797
|
+
// Replace app layout
|
|
798
|
+
const layoutPath = path.join(projectDir, "src", "app", "layout.tsx");
|
|
799
|
+
await fs.writeFile(layoutPath, getLayoutTemplate(kendoThemePackage), "utf8");
|
|
800
|
+
ora("Updated app layout with Kendo theme import").succeed();
|
|
801
|
+
// Create providers component
|
|
802
|
+
const providersPath = path.join(projectDir, "src", "app", "providers.tsx");
|
|
803
|
+
await fs.writeFile(providersPath, getProvidersTemplate(), "utf8");
|
|
804
|
+
ora("Created providers component").succeed();
|
|
805
|
+
// Replace page template
|
|
806
|
+
const pagePath = path.join(projectDir, "src", "app", "page.tsx");
|
|
807
|
+
await fs.writeFile(pagePath, getPageTemplate(), "utf8");
|
|
808
|
+
ora("Updated home page with Portal integration").succeed();
|
|
809
|
+
// Update globals.css
|
|
810
|
+
const globalsCssPath = path.join(projectDir, "src", "app", "globals.css");
|
|
811
|
+
await fs.writeFile(globalsCssPath, getGlobalsCSS(), "utf8");
|
|
812
|
+
ora("Updated globals.css").succeed();
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
// Shadcn/ui setup
|
|
816
|
+
runCommand("npx shadcn@latest init --force --silent --yes --base-color neutral", "Setting up Shadcn/ui...");
|
|
817
|
+
runCommand("npx shadcn@latest add --all", "Installing all Shadcn/ui components...");
|
|
818
|
+
// Replace app layout
|
|
819
|
+
const layoutPath = path.join(projectDir, "src", "app", "layout.tsx");
|
|
820
|
+
await fs.writeFile(layoutPath, getShadcnLayoutTemplate(), "utf8");
|
|
821
|
+
ora("Updated app layout for Shadcn/ui").succeed();
|
|
822
|
+
// Create providers component
|
|
823
|
+
const providersPath = path.join(projectDir, "src", "app", "providers.tsx");
|
|
824
|
+
await fs.writeFile(providersPath, getShadcnProvidersTemplate(), "utf8");
|
|
825
|
+
ora("Created providers component").succeed();
|
|
826
|
+
// Replace page template
|
|
827
|
+
const pagePath = path.join(projectDir, "src", "app", "page.tsx");
|
|
828
|
+
await fs.writeFile(pagePath, getShadcnPageTemplate(), "utf8");
|
|
829
|
+
ora("Updated home page with Shadcn/ui integration").succeed();
|
|
830
|
+
}
|
|
831
|
+
// Add .prettierrc
|
|
832
|
+
const prettierRcPath = path.join(projectDir, ".prettierrc");
|
|
833
|
+
await fs.writeFile(prettierRcPath, getPrettierConfig(), "utf8");
|
|
834
|
+
ora("Added .prettierrc configuration").succeed();
|
|
835
|
+
// Add Compiler Options
|
|
836
|
+
const tsConfigPath = path.join(projectDir, "tsconfig.json");
|
|
837
|
+
await fs.writeFile(tsConfigPath, compilerOptions(), "utf8");
|
|
838
|
+
ora("Added TypeScript compiler options").succeed();
|
|
839
|
+
// Add .env.local
|
|
840
|
+
const envLocalPath = path.join(projectDir, ".env.local");
|
|
841
|
+
await fs.writeFile(envLocalPath, getEnvConfig(), "utf8");
|
|
842
|
+
ora("Added .env.local configuration").succeed();
|
|
843
|
+
runCommand("npx auth secret", "Adding secret to .env.local");
|
|
844
|
+
// Add auth config file
|
|
845
|
+
const authConfigPath = path.join(projectDir, "auth.ts");
|
|
846
|
+
await fs.writeFile(authConfigPath, authConfig(), "utf8");
|
|
847
|
+
ora("Added auth.ts configuration").succeed();
|
|
848
|
+
// Add NextAuth route handler
|
|
849
|
+
const authRouteDir = path.join(projectDir, "src", "app", "api", "auth", "[...nextauth]");
|
|
850
|
+
await fs.ensureDir(authRouteDir);
|
|
851
|
+
const authRoutePath = path.join(authRouteDir, "route.ts");
|
|
852
|
+
await fs.writeFile(authRoutePath, getAuthRoute(), "utf8");
|
|
853
|
+
ora("Added NextAuth route handler").succeed();
|
|
854
|
+
// Add Dynamics accounts API route
|
|
855
|
+
const dynamicsAccountsRouteDir = path.join(projectDir, "src", "app", "api", "dynamics", "accounts");
|
|
856
|
+
await fs.ensureDir(dynamicsAccountsRouteDir);
|
|
857
|
+
const dynamicsAccountsRoutePath = path.join(dynamicsAccountsRouteDir, "route.ts");
|
|
858
|
+
await fs.writeFile(dynamicsAccountsRoutePath, getDynamicsAccountsRoute(), "utf8");
|
|
859
|
+
ora("Added Dynamics accounts API route").succeed();
|
|
860
|
+
// Add next.config.ts with standalone settings
|
|
861
|
+
const nextConfigPath = path.join(projectDir, "next.config.ts");
|
|
862
|
+
await fs.writeFile(nextConfigPath, getNextConfig(), "utf8");
|
|
863
|
+
ora("Updated next.config.ts with standalone settings").succeed();
|
|
864
|
+
// Add github workflow for deployment
|
|
865
|
+
const workflowFilePath = path.join(projectDir, "example.deploy.yml");
|
|
866
|
+
await fs.writeFile(workflowFilePath, getGithubWorkflow(), "utf8");
|
|
867
|
+
ora("Added GitHub workflow for Azure deployment").succeed();
|
|
868
|
+
// Add Dynamics lib helper
|
|
869
|
+
const dynamicsLibDir = path.join(projectDir, "src", "lib");
|
|
870
|
+
await fs.ensureDir(dynamicsLibDir);
|
|
871
|
+
const dynamicsLibPath = path.join(dynamicsLibDir, "dynamics.ts");
|
|
872
|
+
await fs.writeFile(dynamicsLibPath, getDynamicsLib(), "utf8");
|
|
873
|
+
ora("Added Dynamics library helper").succeed();
|
|
874
|
+
// Add Dynamics accounts React Query hook
|
|
875
|
+
const hooksDir = path.join(projectDir, "src", "hooks");
|
|
876
|
+
await fs.ensureDir(hooksDir);
|
|
877
|
+
const dynamicsAccountsHookPath = path.join(hooksDir, "useDynamicsAccounts.ts");
|
|
878
|
+
await fs.writeFile(dynamicsAccountsHookPath, getDynamicsAccountsHook(), "utf8");
|
|
879
|
+
ora("Added Dynamics accounts React Query hook").succeed();
|
|
880
|
+
// Initialize Git
|
|
881
|
+
runCommand("git init", "Initializing Git repository...");
|
|
882
|
+
runCommand("git add .", "Staging files for initial commit...");
|
|
883
|
+
runCommand(`git commit -m "Initial commit from create-ec-app"`, "Creating initial commit...");
|
|
884
|
+
};
|
|
885
|
+
//# sourceMappingURL=portal.js.map
|