create-ec-app 0.0.5 → 0.0.6

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