Jensin commited on
Commit
2203154
·
verified ·
1 Parent(s): 89c93e0

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +994 -19
index.html CHANGED
@@ -1,19 +1,994 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-alpine AS base
2
+
3
+ FROM base AS deps
4
+ RUN apk add --no-cache libc6-compat
5
+ WORKDIR /app
6
+
7
+ COPY package.json package-lock.json* ./
8
+ RUN npm ci
9
+
10
+ FROM base AS builder
11
+ WORKDIR /app
12
+ COPY --from=deps /app/node_modules ./node_modules
13
+ COPY . .
14
+
15
+ RUN npm run build
16
+
17
+ FROM base AS runner
18
+ WORKDIR /app
19
+
20
+ ENV NODE_ENV=production
21
+ ENV PORT=7860
22
+ ENV HOSTNAME="0.0.0.0"
23
+
24
+ RUN addgroup --system --gid 1001 nodejs
25
+ RUN adduser --system --uid 1001 nextjs
26
+
27
+ COPY --from=builder /app/public ./public
28
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
29
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
30
+
31
+ USER nextjs
32
+
33
+ EXPOSE 7860
34
+
35
+ CMD ["node", "server.js"]
36
+
37
+ === package.json ===
38
+ {
39
+ "name": "ai-backend-generator",
40
+ "version": "1.0.0",
41
+ "private": true,
42
+ "scripts": {
43
+ "dev": "next dev",
44
+ "build": "next build",
45
+ "start": "next start",
46
+ "lint": "next lint"
47
+ },
48
+ "dependencies": {
49
+ "next": "14.2.3",
50
+ "react": "18.3.1",
51
+ "react-dom": "18.3.1",
52
+ "openai": "4.52.0",
53
+ "zod": "3.23.8",
54
+ "prismjs": "1.29.0",
55
+ "react-flow-renderer": "10.3.17",
56
+ "lucide-react": "0.378.0",
57
+ "jszip": "3.10.1",
58
+ "file-saver": "2.0.5"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "20.12.12",
62
+ "@types/react": "18.3.2",
63
+ "@types/react-dom": "18.3.0",
64
+ "@types/prismjs": "1.26.4",
65
+ "@types/file-saver": "2.0.7",
66
+ "typescript": "5.4.5",
67
+ "tailwindcss": "3.4.3",
68
+ "postcss": "8.4.38",
69
+ "autoprefixer": "10.4.19"
70
+ }
71
+ }
72
+
73
+ === next.config.js ===
74
+ /** @type {import('next').NextConfig} */
75
+ const nextConfig = {
76
+ output: 'standalone',
77
+ reactStrictMode: true,
78
+ swcMinify: true,
79
+ experimental: {
80
+ serverComponentsExternalPackages: ['openai']
81
+ }
82
+ }
83
+
84
+ module.exports = nextConfig
85
+
86
+ === postcss.config.js ===
87
+ module.exports = {
88
+ plugins: {
89
+ tailwindcss: {},
90
+ autoprefixer: {},
91
+ },
92
+ }
93
+
94
+ === tailwind.config.js ===
95
+ /** @type {import('tailwindcss').Config} */
96
+ module.exports = {
97
+ content: [
98
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
99
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
100
+ ],
101
+ theme: {
102
+ extend: {
103
+ colors: {
104
+ primary: {
105
+ 50: '#f0f9ff',
106
+ 100: '#e0f2fe',
107
+ 200: '#bae6fd',
108
+ 300: '#7dd3fc',
109
+ 400: '#38bdf8',
110
+ 500: '#0ea5e9',
111
+ 600: '#0284c7',
112
+ 700: '#0369a1',
113
+ 800: '#075985',
114
+ 900: '#0c4a6e',
115
+ },
116
+ dark: {
117
+ 50: '#f8fafc',
118
+ 100: '#f1f5f9',
119
+ 200: '#e2e8f0',
120
+ 300: '#cbd5e1',
121
+ 400: '#94a3b8',
122
+ 500: '#64748b',
123
+ 600: '#475569',
124
+ 700: '#334155',
125
+ 800: '#1e293b',
126
+ 900: '#0f172a',
127
+ 950: '#020617',
128
+ }
129
+ },
130
+ animation: {
131
+ 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
132
+ 'float': 'float 6s ease-in-out infinite',
133
+ 'glow': 'glow 2s ease-in-out infinite alternate',
134
+ },
135
+ keyframes: {
136
+ float: {
137
+ '0%, 100%': { transform: 'translateY(0)' },
138
+ '50%': { transform: 'translateY(-10px)' },
139
+ },
140
+ glow: {
141
+ '0%': { boxShadow: '0 0 5px rgba(14, 165, 233, 0.5)' },
142
+ '100%': { boxShadow: '0 0 20px rgba(14, 165, 233, 0.8)' },
143
+ }
144
+ }
145
+ },
146
+ },
147
+ plugins: [],
148
+ }
149
+
150
+ === tsconfig.json ===
151
+ {
152
+ "compilerOptions": {
153
+ "target": "es5",
154
+ "lib": ["dom", "dom.iterable", "esnext"],
155
+ "allowJs": true,
156
+ "skipLibCheck": true,
157
+ "strict": true,
158
+ "noEmit": true,
159
+ "esModuleInterop": true,
160
+ "module": "esnext",
161
+ "moduleResolution": "bundler",
162
+ "resolveJsonModule": true,
163
+ "isolatedModules": true,
164
+ "jsx": "preserve",
165
+ "incremental": true,
166
+ "paths": {
167
+ "@/*": ["./*"]
168
+ }
169
+ },
170
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
171
+ "exclude": ["node_modules"]
172
+ }
173
+
174
+ === styles/globals.css ===
175
+ @tailwind base;
176
+ @tailwind components;
177
+ @tailwind utilities;
178
+
179
+ @layer base {
180
+ html {
181
+ scroll-behavior: smooth;
182
+ }
183
+
184
+ body {
185
+ @apply bg-dark-950 text-dark-100 antialiased;
186
+ }
187
+
188
+ ::-webkit-scrollbar {
189
+ width: 8px;
190
+ height: 8px;
191
+ }
192
+
193
+ ::-webkit-scrollbar-track {
194
+ @apply bg-dark-900;
195
+ }
196
+
197
+ ::-webkit-scrollbar-thumb {
198
+ @apply bg-dark-600 rounded-full;
199
+ }
200
+
201
+ ::-webkit-scrollbar-thumb:hover {
202
+ @apply bg-dark-500;
203
+ }
204
+ }
205
+
206
+ @layer components {
207
+ .glass-panel {
208
+ @apply bg-dark-900/80 backdrop-blur-xl border border-dark-700/50 rounded-2xl;
209
+ }
210
+
211
+ .gradient-border {
212
+ @apply relative;
213
+ }
214
+
215
+ .gradient-border::before {
216
+ content: '';
217
+ @apply absolute inset-0 rounded-2xl p-[1px];
218
+ background: linear-gradient(135deg, rgba(14, 165, 233, 0.5), rgba(139, 92, 246, 0.5));
219
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
220
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
221
+ -webkit-mask-composite: xor;
222
+ mask-composite: exclude;
223
+ }
224
+
225
+ .btn-primary {
226
+ @apply px-6 py-3 bg-gradient-to-r from-primary-500 to-primary-600 text-white font-semibold rounded-xl
227
+ hover:from-primary-600 hover:to-primary-700 transition-all duration-300 shadow-lg shadow-primary-500/25
228
+ disabled:opacity-50 disabled:cursor-not-allowed;
229
+ }
230
+
231
+ .btn-secondary {
232
+ @apply px-6 py-3 bg-dark-800 text-dark-100 font-semibold rounded-xl border border-dark-600
233
+ hover:bg-dark-700 hover:border-dark-500 transition-all duration-300;
234
+ }
235
+
236
+ .input-field {
237
+ @apply w-full px-4 py-3 bg-dark-800/50 border border-dark-600 rounded-xl text-dark-100
238
+ placeholder-dark-400 focus:outline-none focus:ring-2 focus:ring-primary-500/50 focus:border-primary-500
239
+ transition-all duration-300;
240
+ }
241
+
242
+ .code-block {
243
+ @apply bg-dark-900 rounded-xl p-4 font-mono text-sm overflow-x-auto border border-dark-700;
244
+ }
245
+ }
246
+
247
+ /* Prism.js custom theme */
248
+ code[class*="language-"],
249
+ pre[class*="language-"] {
250
+ @apply text-dark-100 font-mono text-sm;
251
+ text-shadow: none;
252
+ }
253
+
254
+ .token.comment,
255
+ .token.prolog,
256
+ .token.doctype,
257
+ .token.cdata {
258
+ @apply text-dark-500;
259
+ }
260
+
261
+ .token.punctuation {
262
+ @apply text-dark-300;
263
+ }
264
+
265
+ .token.property,
266
+ .token.tag,
267
+ .token.boolean,
268
+ .token.number,
269
+ .token.constant,
270
+ .token.symbol,
271
+ .token.deleted {
272
+ @apply text-pink-400;
273
+ }
274
+
275
+ .token.selector,
276
+ .token.attr-name,
277
+ .token.string,
278
+ .token.char,
279
+ .token.builtin,
280
+ .token.inserted {
281
+ @apply text-green-400;
282
+ }
283
+
284
+ .token.operator,
285
+ .token.entity,
286
+ .token.url,
287
+ .language-css .token.string,
288
+ .style .token.string {
289
+ @apply text-yellow-400;
290
+ }
291
+
292
+ .token.atrule,
293
+ .token.attr-value,
294
+ .token.keyword {
295
+ @apply text-primary-400;
296
+ }
297
+
298
+ .token.function,
299
+ .token.class-name {
300
+ @apply text-purple-400;
301
+ }
302
+
303
+ .token.regex,
304
+ .token.important,
305
+ .token.variable {
306
+ @apply text-orange-400;
307
+ }
308
+
309
+ === components/Header.jsx ===
310
+ import { Database, Sparkles } from 'lucide-react';
311
+
312
+ export default function Header() {
313
+ return (
314
+ <header className="fixed top-0 left-0 right-0 z-50 glass-panel border-t-0 rounded-t-none">
315
+ <div className="max-w-7xl mx-auto px-6 py-4">
316
+ <div className="flex items-center justify-between">
317
+ <div className="flex items-center gap-3">
318
+ <div className="relative">
319
+ <div className="w-10 h-10 bg-gradient-to-br from-primary-500 to-purple-600 rounded-xl flex items-center justify-center">
320
+ <Database className="w-5 h-5 text-white" />
321
+ </div>
322
+ <div className="absolute -top-1 -right-1 w-4 h-4 bg-gradient-to-br from-yellow-400 to-orange-500 rounded-full flex items-center justify-center">
323
+ <Sparkles className="w-2.5 h-2.5 text-white" />
324
+ </div>
325
+ </div>
326
+ <div>
327
+ <h1 className="text-xl font-bold bg-gradient-to-r from-white to-dark-300 bg-clip-text text-transparent">
328
+ AI Backend Generator
329
+ </h1>
330
+ <p className="text-xs text-dark-400">Database + API Design Copilot</p>
331
+ </div>
332
+ </div>
333
+ <div className="flex items-center gap-4">
334
+ <a
335
+ href="https://huggingface.co/spaces/akhaliq/anycoder"
336
+ target="_blank"
337
+ rel="noopener noreferrer"
338
+ className="text-sm text-dark-400 hover:text-primary-400 transition-colors"
339
+ >
340
+ Built with anycoder
341
+ </a>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </header>
346
+ );
347
+ }
348
+
349
+ === components/ProjectWizard.jsx ===
350
+ import { useState } from 'react';
351
+ import { ArrowRight, Loader2, Wand2, FileCode, Database, Server } from 'lucide-react';
352
+
353
+ export default function ProjectWizard({ onGenerate, isLoading }) {
354
+ const [projectDescription, setProjectDescription] = useState('');
355
+ const [projectName, setProjectName] = useState('');
356
+ const [targetStack, setTargetStack] = useState('nestjs-prisma');
357
+
358
+ const handleSubmit = (e) => {
359
+ e.preventDefault();
360
+ if (!projectDescription.trim() || !projectName.trim()) return;
361
+ onGenerate({ projectName, projectDescription, targetStack });
362
+ };
363
+
364
+ const examples = [
365
+ {
366
+ name: 'E-commerce Platform',
367
+ description: 'An e-commerce platform with products, categories, users, orders, shopping cart, reviews, and payment tracking. Include inventory management and order status tracking.',
368
+ },
369
+ {
370
+ name: 'Project Management Tool',
371
+ description: 'A project management tool like Jira with workspaces, projects, tasks, sprints, users, comments, attachments, and time tracking. Include role-based permissions.',
372
+ },
373
+ {
374
+ name: 'Social Media App',
375
+ description: 'A social media application with users, posts, comments, likes, followers/following relationships, direct messages, and notifications.',
376
+ },
377
+ ];
378
+
379
+ return (
380
+ <div className="glass-panel gradient-border p-8">
381
+ <div className="flex items-center gap-3 mb-6">
382
+ <div className="w-12 h-12 bg-gradient-to-br from-primary-500/20 to-purple-500/20 rounded-xl flex items-center justify-center">
383
+ <Wand2 className="w-6 h-6 text-primary-400" />
384
+ </div>
385
+ <div>
386
+ <h2 className="text-2xl font-bold text-white">Describe Your Project</h2>
387
+ <p className="text-dark-400">Tell us what you're building and we'll design the backend</p>
388
+ </div>
389
+ </div>
390
+
391
+ <form onSubmit={handleSubmit} className="space-y-6">
392
+ <div>
393
+ <label className="block text-sm font-medium text-dark-300 mb-2">
394
+ Project Name
395
+ </label>
396
+ <input
397
+ type="text"
398
+ value={projectName}
399
+ onChange={(e) => setProjectName(e.target.value)}
400
+ placeholder="my-awesome-app"
401
+ className="input-field"
402
+ disabled={isLoading}
403
+ />
404
+ </div>
405
+
406
+ <div>
407
+ <label className="block text-sm font-medium text-dark-300 mb-2">
408
+ Project Description
409
+ </label>
410
+ <textarea
411
+ value={projectDescription}
412
+ onChange={(e) => setProjectDescription(e.target.value)}
413
+ placeholder="Describe your project in detail. What entities do you need? What are the main features? What relationships exist between data?"
414
+ rows={6}
415
+ className="input-field resize-none"
416
+ disabled={isLoading}
417
+ />
418
+ </div>
419
+
420
+ <div>
421
+ <label className="block text-sm font-medium text-dark-300 mb-2">
422
+ Target Stack
423
+ </label>
424
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
425
+ <button
426
+ type="button"
427
+ onClick={() => setTargetStack('nestjs-prisma')}
428
+ disabled={isLoading}
429
+ className={`p-4 rounded-xl border transition-all duration-300 text-left ${
430
+ targetStack === 'nestjs-prisma'
431
+ ? 'border-primary-500 bg-primary-500/10'
432
+ : 'border-dark-600 bg-dark-800/50 hover:border-dark-500'
433
+ }`}
434
+ >
435
+ <div className="flex items-center gap-3 mb-2">
436
+ <Server className="w-5 h-5 text-primary-400" />
437
+ <span className="font-semibold text-white">NestJS + Prisma</span>
438
+ </div>
439
+ <p className="text-sm text-dark-400">
440
+ TypeScript, NestJS framework, Prisma ORM, PostgreSQL
441
+ </p>
442
+ </button>
443
+ <button
444
+ type="button"
445
+ onClick={() => setTargetStack('express-drizzle')}
446
+ disabled={isLoading}
447
+ className={`p-4 rounded-xl border transition-all duration-300 text-left ${
448
+ targetStack === 'express-drizzle'
449
+ ? 'border-primary-500 bg-primary-500/10'
450
+ : 'border-dark-600 bg-dark-800/50 hover:border-dark-500'
451
+ }`}
452
+ >
453
+ <div className="flex items-center gap-3 mb-2">
454
+ <FileCode className="w-5 h-5 text-green-400" />
455
+ <span className="font-semibold text-white">Express + Drizzle</span>
456
+ </div>
457
+ <p className="text-sm text-dark-400">
458
+ TypeScript, Express.js, Drizzle ORM, PostgreSQL
459
+ </p>
460
+ </button>
461
+ </div>
462
+ </div>
463
+
464
+ <div>
465
+ <p className="text-sm text-dark-400 mb-3">Quick examples:</p>
466
+ <div className="flex flex-wrap gap-2">
467
+ {examples.map((example) => (
468
+ <button
469
+ key={example.name}
470
+ type="button"
471
+ onClick={() => {
472
+ setProjectName(example.name.toLowerCase().replace(/\s+/g, '-'));
473
+ setProjectDescription(example.description);
474
+ }}
475
+ disabled={isLoading}
476
+ className="px-3 py-1.5 text-sm bg-dark-800 text-dark-300 rounded-lg border border-dark-600
477
+ hover:border-primary-500/50 hover:text-primary-400 transition-all duration-300"
478
+ >
479
+ {example.name}
480
+ </button>
481
+ ))}
482
+ </div>
483
+ </div>
484
+
485
+ <button
486
+ type="submit"
487
+ disabled={isLoading || !projectDescription.trim() || !projectName.trim()}
488
+ className="btn-primary w-full flex items-center justify-center gap-2"
489
+ >
490
+ {isLoading ? (
491
+ <>
492
+ <Loader2 className="w-5 h-5 animate-spin" />
493
+ Generating Backend...
494
+ </>
495
+ ) : (
496
+ <>
497
+ Generate Backend
498
+ <ArrowRight className="w-5 h-5" />
499
+ </>
500
+ )}
501
+ </button>
502
+ </form>
503
+ </div>
504
+ );
505
+ }
506
+
507
+ === components/SchemaVisualizer.jsx ===
508
+ import { useCallback, useMemo } from 'react';
509
+ import ReactFlow, {
510
+ Background,
511
+ Controls,
512
+ MiniMap,
513
+ useNodesState,
514
+ useEdgesState,
515
+ } from 'react-flow-renderer';
516
+ import { Database, Key, Link2 } from 'lucide-react';
517
+
518
+ const EntityNode = ({ data }) => {
519
+ return (
520
+ <div className="bg-dark-800 border border-dark-600 rounded-xl shadow-xl min-w-[200px] overflow-hidden">
521
+ <div className="bg-gradient-to-r from-primary-600 to-purple-600 px-4 py-2 flex items-center gap-2">
522
+ <Database className="w-4 h-4 text-white" />
523
+ <span className="font-bold text-white text-sm">{data.name}</span>
524
+ </div>
525
+ <div className="p-3 space-y-1.5">
526
+ {data.fields?.map((field, idx) => (
527
+ <div key={idx} className="flex items-center gap-2 text-xs">
528
+ {field.isPrimary && <Key className="w-3 h-3 text-yellow-400" />}
529
+ {field.isRelation && <Link2 className="w-3 h-3 text-primary-400" />}
530
+ <span className={`${field.isPrimary ? 'text-yellow-400' : 'text-dark-200'}`}>
531
+ {field.name}
532
+ </span>
533
+ <span className="text-dark-500">{field.type}</span>
534
+ </div>
535
+ ))}
536
+ </div>
537
+ </div>
538
+ );
539
+ };
540
+
541
+ const nodeTypes = {
542
+ entity: EntityNode,
543
+ };
544
+
545
+ export default function SchemaVisualizer({ schema }) {
546
+ const { nodes: initialNodes, edges: initialEdges } = useMemo(() => {
547
+ if (!schema?.entities) return { nodes: [], edges: [] };
548
+
549
+ const nodes = schema.entities.map((entity, idx) => ({
550
+ id: entity.name,
551
+ type: 'entity',
552
+ position: {
553
+ x: (idx % 3) * 300 + 50,
554
+ y: Math.floor(idx / 3) * 250 + 50,
555
+ },
556
+ data: {
557
+ name: entity.name,
558
+ fields: entity.fields,
559
+ },
560
+ }));
561
+
562
+ const edges = [];
563
+ schema.entities.forEach((entity) => {
564
+ entity.fields
565
+ ?.filter((f) => f.isRelation && f.relationTo)
566
+ .forEach((field, idx) => {
567
+ edges.push({
568
+ id: `${entity.name}-${field.relationTo}-${idx}`,
569
+ source: entity.name,
570
+ target: field.relationTo,
571
+ type: 'smoothstep',
572
+ animated: true,
573
+ style: { stroke: '#0ea5e9', strokeWidth: 2 },
574
+ });
575
+ });
576
+ });
577
+
578
+ return { nodes, edges };
579
+ }, [schema]);
580
+
581
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
582
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
583
+
584
+ if (!schema?.entities?.length) {
585
+ return (
586
+ <div className="h-[400px] flex items-center justify-center text-dark-400">
587
+ <p>No schema to visualize yet</p>
588
+ </div>
589
+ );
590
+ }
591
+
592
+ return (
593
+ <div className="h-[500px] bg-dark-900 rounded-xl border border-dark-700 overflow-hidden">
594
+ <ReactFlow
595
+ nodes={nodes}
596
+ edges={edges}
597
+ onNodesChange={onNodesChange}
598
+ onEdgesChange={onEdgesChange}
599
+ nodeTypes={nodeTypes}
600
+ fitView
601
+ attributionPosition="bottom-left"
602
+ >
603
+ <Background color="#334155" gap={20} />
604
+ <Controls className="bg-dark-800 border-dark-600" />
605
+ <MiniMap
606
+ nodeColor="#0ea5e9"
607
+ maskColor="rgba(15, 23, 42, 0.8)"
608
+ className="bg-dark-800 border-dark-600"
609
+ />
610
+ </ReactFlow>
611
+ </div>
612
+ );
613
+ }
614
+
615
+ === components/CodeViewer.jsx ===
616
+ import { useEffect, useRef, useState } from 'react';
617
+ import Prism from 'prismjs';
618
+ import 'prismjs/components/prism-typescript';
619
+ import 'prismjs/components/prism-javascript';
620
+ import 'prismjs/components/prism-json';
621
+ import 'prismjs/components/prism-bash';
622
+ import 'prismjs/components/prism-sql';
623
+ import { Copy, Check, FileCode } from 'lucide-react';
624
+
625
+ export default function CodeViewer({ code, language = 'typescript', filename }) {
626
+ const codeRef = useRef(null);
627
+ const [copied, setCopied] = useState(false);
628
+
629
+ useEffect(() => {
630
+ if (codeRef.current) {
631
+ Prism.highlightElement(codeRef.current);
632
+ }
633
+ }, [code]);
634
+
635
+ const handleCopy = async () => {
636
+ await navigator.clipboard.writeText(code);
637
+ setCopied(true);
638
+ setTimeout(() => setCopied(false), 2000);
639
+ };
640
+
641
+ return (
642
+ <div className="rounded-xl border border-dark-700 overflow-hidden">
643
+ <div className="bg-dark-800 px-4 py-2 flex items-center justify-between border-b border-dark-700">
644
+ <div className="flex items-center gap-2">
645
+ <FileCode className="w-4 h-4 text-dark-400" />
646
+ <span className="text-sm text-dark-300 font-mono">{filename}</span>
647
+ </div>
648
+ <button
649
+ onClick={handleCopy}
650
+ className="p-1.5 rounded-lg hover:bg-dark-700 transition-colors"
651
+ >
652
+ {copied ? (
653
+ <Check className="w-4 h-4 text-green-400" />
654
+ ) : (
655
+ <Copy className="w-4 h-4 text-dark-400" />
656
+ )}
657
+ </button>
658
+ </div>
659
+ <div className="code-block max-h-[400px] overflow-auto">
660
+ <pre className="!bg-transparent !p-0 !m-0">
661
+ <code ref={codeRef} className={`language-${language}`}>
662
+ {code}
663
+ </code>
664
+ </pre>
665
+ </div>
666
+ </div>
667
+ );
668
+ }
669
+
670
+ === components/GeneratedOutput.jsx ===
671
+ import { useState } from 'react';
672
+ import { Download, Github, Database, Server, FileJson, Code2, Layers, ChevronDown, ChevronUp } from 'lucide-react';
673
+ import CodeViewer from './CodeViewer';
674
+ import SchemaVisualizer from './SchemaVisualizer';
675
+
676
+ export default function GeneratedOutput({ result, onDownload }) {
677
+ const [activeTab, setActiveTab] = useState('schema');
678
+ const [expandedFiles, setExpandedFiles] = useState({});
679
+
680
+ const tabs = [
681
+ { id: 'schema', label: 'Schema Diagram', icon: Database },
682
+ { id: 'prisma', label: 'Prisma Schema', icon: Layers },
683
+ { id: 'api', label: 'API Routes', icon: Server },
684
+ { id: 'openapi', label: 'OpenAPI Spec', icon: FileJson },
685
+ { id: 'code', label: 'Generated Code', icon: Code2 },
686
+ ];
687
+
688
+ const toggleFile = (filename) => {
689
+ setExpandedFiles((prev) => ({
690
+ ...prev,
691
+ [filename]: !prev[filename],
692
+ }));
693
+ };
694
+
695
+ return (
696
+ <div className="space-y-6">
697
+ <div className="glass-panel gradient-border p-6">
698
+ <div className="flex items-center justify-between mb-6">
699
+ <h2 className="text-xl font-bold text-white">Generated Backend</h2>
700
+ <div className="flex gap-3">
701
+ <button onClick={onDownload} className="btn-secondary flex items-center gap-2">
702
+ <Download className="w-4 h-4" />
703
+ Download ZIP
704
+ </button>
705
+ </div>
706
+ </div>
707
+
708
+ <div className="flex gap-2 mb-6 overflow-x-auto pb-2">
709
+ {tabs.map((tab) => (
710
+ <button
711
+ key={tab.id}
712
+ onClick={() => setActiveTab(tab.id)}
713
+ className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all whitespace-nowrap ${
714
+ activeTab === tab.id
715
+ ? 'bg-primary-500/20 text-primary-400 border border-primary-500/50'
716
+ : 'bg-dark-800 text-dark-400 border border-dark-700 hover:border-dark-500'
717
+ }`}
718
+ >
719
+ <tab.icon className="w-4 h-4" />
720
+ {tab.label}
721
+ </button>
722
+ ))}
723
+ </div>
724
+
725
+ <div className="min-h-[400px]">
726
+ {activeTab === 'schema' && (
727
+ <SchemaVisualizer schema={result.schema} />
728
+ )}
729
+
730
+ {activeTab === 'prisma' && result.prismaSchema && (
731
+ <CodeViewer
732
+ code={result.prismaSchema}
733
+ language="typescript"
734
+ filename="prisma/schema.prisma"
735
+ />
736
+ )}
737
+
738
+ {activeTab === 'api' && result.apiRoutes && (
739
+ <div className="space-y-4">
740
+ {result.apiRoutes.map((route, idx) => (
741
+ <div
742
+ key={idx}
743
+ className="bg-dark-800/50 rounded-xl border border-dark-700 p-4"
744
+ >
745
+ <div className="flex items-center gap-3 mb-2">
746
+ <span
747
+ className={`px-2 py-0.5 rounded text-xs font-bold ${
748
+ route.method === 'GET'
749
+ ? 'bg-green-500/20 text-green-400'
750
+ : route.method === 'POST'
751
+ ? 'bg-blue-500/20 text-blue-400'
752
+ : route.method === 'PUT' || route.method === 'PATCH'
753
+ ? 'bg-yellow-500/20 text-yellow-400'
754
+ : 'bg-red-500/20 text-red-400'
755
+ }`}
756
+ >
757
+ {route.method}
758
+ </span>
759
+ <code className="text-dark-200 font-mono text-sm">
760
+ {route.path}
761
+ </code>
762
+ </div>
763
+ <p className="text-dark-400 text-sm">{route.description}</p>
764
+ </div>
765
+ ))}
766
+ </div>
767
+ )}
768
+
769
+ {activeTab === 'openapi' && result.openApiSpec && (
770
+ <CodeViewer
771
+ code={JSON.stringify(result.openApiSpec, null, 2)}
772
+ language="json"
773
+ filename="openapi.json"
774
+ />
775
+ )}
776
+
777
+ {activeTab === 'code' && result.generatedFiles && (
778
+ <div className="space-y-3">
779
+ {result.generatedFiles.map((file, idx) => (
780
+ <div
781
+ key={idx}
782
+ className="bg-dark-800/50 rounded-xl border border-dark-700 overflow-hidden"
783
+ >
784
+ <button
785
+ onClick={() => toggleFile(file.filename)}
786
+ className="w-full px-4 py-3 flex items-center justify-between hover:bg-dark-700/50 transition-colors"
787
+ >
788
+ <div className="flex items-center gap-2">
789
+ <Code2 className="w-4 h-4 text-primary-400" />
790
+ <span className="text-dark-200 font-mono text-sm">
791
+ {file.filename}
792
+ </span>
793
+ </div>
794
+ {expandedFiles[file.filename] ? (
795
+ <ChevronUp className="w-4 h-4 text-dark-400" />
796
+ ) : (
797
+ <ChevronDown className="w-4 h-4 text-dark-400" />
798
+ )}
799
+ </button>
800
+ {expandedFiles[file.filename] && (
801
+ <div className="border-t border-dark-700">
802
+ <CodeViewer
803
+ code={file.content}
804
+ language={file.language || 'typescript'}
805
+ filename={file.filename}
806
+ />
807
+ </div>
808
+ )}
809
+ </div>
810
+ ))}
811
+ </div>
812
+ )}
813
+ </div>
814
+ </div>
815
+ </div>
816
+ );
817
+ }
818
+
819
+ === components/LoadingState.jsx ===
820
+ import { Database, Server, FileCode, Sparkles, Check } from 'lucide-react';
821
+
822
+ export default function LoadingState({ currentStep }) {
823
+ const steps = [
824
+ { id: 'analyzing', label: 'Analyzing requirements', icon: Sparkles },
825
+ { id: 'schema', label: 'Designing database schema', icon: Database },
826
+ { id: 'api', label: 'Generating API routes', icon: Server },
827
+ { id: 'code', label: 'Writing backend code', icon: FileCode },
828
+ ];
829
+
830
+ const currentIndex = steps.findIndex((s) => s.id === currentStep);
831
+
832
+ return (
833
+ <div className="glass-panel gradient-border p-8">
834
+ <div className="text-center mb-8">
835
+ <div className="w-16 h-16 mx-auto mb-4 relative">
836
+ <div className="absolute inset-0 bg-gradient-to-r from-primary-500 to-purple-500 rounded-full animate-pulse-slow" />
837
+ <div className="absolute inset-1 bg-dark-900 rounded-full flex items-center justify-center">
838
+ <Sparkles className="w-6 h-6 text-primary-400 animate-float" />
839
+ </div>
840
+ </div>
841
+ <h3 className="text-xl font-bold text-white mb-2">AI is working its magic</h3>
842
+ <p className="text-dark-400">Designing your backend architecture...</p>
843
+ </div>
844
+
845
+ <div className="space-y-4">
846
+ {steps.map((step, idx) => {
847
+ const isCompleted = idx < currentIndex;
848
+ const isCurrent = idx === currentIndex;
849
+
850
+ return (
851
+ <div
852
+ key={step.id}
853
+ className={`flex items-center gap-4 p-4 rounded-xl transition-all duration-500 ${
854
+ isCurrent
855
+ ? 'bg-primary-500/10 border border-primary-500/30'
856
+ : isCompleted
857
+ ? 'bg-green-500/10 border border-green-500/30'
858
+ : 'bg-dark-800/50 border border-dark-700'
859
+ }`}
860
+ >
861
+ <div
862
+ className={`w-10 h-10 rounded-xl flex items-center justify-center ${
863
+ isCurrent
864
+ ? 'bg-primary-500/20'
865
+ : isCompleted
866
+ ? 'bg-green-500/20'
867
+ : 'bg-dark-700'
868
+ }`}
869
+ >
870
+ {isCompleted ? (
871
+ <Check className="w-5 h-5 text-green-400" />
872
+ ) : (
873
+ <step.icon
874
+ className={`w-5 h-5 ${
875
+ isCurrent ? 'text-primary-400 animate-pulse' : 'text-dark-500'
876
+ }`}
877
+ />
878
+ )}
879
+ </div>
880
+ <span
881
+ className={`font-medium ${
882
+ isCurrent
883
+ ? 'text-primary-400'
884
+ : isCompleted
885
+ ? 'text-green-400'
886
+ : 'text-dark-500'
887
+ }`}
888
+ >
889
+ {step.label}
890
+ </span>
891
+ {isCurrent && (
892
+ <div className="ml-auto">
893
+ <div className="w-5 h-5 border-2 border-primary-400 border-t-transparent rounded-full animate-spin" />
894
+ </div>
895
+ )}
896
+ </div>
897
+ );
898
+ })}
899
+ </div>
900
+ </div>
901
+ );
902
+ }
903
+
904
+ === components/FeatureCards.jsx ===
905
+ import { Database, Server, Code2, Zap, Shield, Layers } from 'lucide-react';
906
+
907
+ export default function FeatureCards() {
908
+ const features = [
909
+ {
910
+ icon: Database,
911
+ title: 'Smart Schema Design',
912
+ description: 'AI analyzes your requirements and designs optimal database schemas with proper relationships.',
913
+ color: 'from-blue-500 to-cyan-500',
914
+ },
915
+ {
916
+ icon: Server,
917
+ title: 'Auto API Generation',
918
+ description: 'Generates RESTful APIs with CRUD operations, validation, and error handling built-in.',
919
+ color: 'from-purple-500 to-pink-500',
920
+ },
921
+ {
922
+ icon: Code2,
923
+ title: 'Production-Ready Code',
924
+ description: 'Clean, typed TypeScript code following best practices with NestJS or Express.',
925
+ color: 'from-green-500 to-emerald-500',
926
+ },
927
+ {
928
+ icon: Zap,
929
+ title: 'Instant Setup',
930
+ description: 'Download and run immediately with Docker support and migration scripts included.',
931
+ color: 'from-yellow-500 to-orange-500',
932
+ },
933
+ {
934
+ icon: Shield,
935
+ title: 'Type Safety',
936
+ description: 'Full TypeScript support with Prisma/Drizzle for type-safe database operations.',
937
+ color: 'from-red-500 to-rose-500',
938
+ },
939
+ {
940
+ icon: Layers,
941
+ title: 'OpenAPI Docs',
942
+ description: 'Auto-generated OpenAPI/Swagger documentation for all your endpoints.',
943
+ color: 'from-indigo-500 to-violet-500',
944
+ },
945
+ ];
946
+
947
+ return (
948
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
949
+ {features.map((feature, idx) => (
950
+ <div
951
+ key={idx}
952
+ className="glass-panel p-6 hover:border-dark-500 transition-all duration-300 group"
953
+ >
954
+ <div
955
+ className={`w-12 h-12 rounded-xl bg-gradient-to-br ${feature.color} p-0.5 mb-4
956
+ group-hover:scale-110 transition-transform duration-300`}
957
+ >
958
+ <div className="w-full h-full bg-dark-900 rounded-[10px] flex items-center justify-center">
959
+ <feature.icon className="w-5 h-5 text-white" />
960
+ </div>
961
+ </div>
962
+ <h3 className="text-lg font-semibold text-white mb-2">{feature.title}</h3>
963
+ <p className="text-dark-400 text-sm">{feature.description}</p>
964
+ </div>
965
+ ))}
966
+ </div>
967
+ );
968
+ }
969
+
970
+ === pages/_app.js ===
971
+ import '../styles/globals.css';
972
+
973
+ export default function App({ Component, pageProps }) {
974
+ return <Component {...pageProps} />;
975
+ }
976
+
977
+ === pages/index.js ===
978
+ import { useState, useCallback } from 'react';
979
+ import Head from 'next/head';
980
+ import Header from '../components/Header';
981
+ import ProjectWizard from '../components/ProjectWizard';
982
+ import GeneratedOutput from '../components/GeneratedOutput';
983
+ import LoadingState from '../components/LoadingState';
984
+ import FeatureCards from '../components/FeatureCards';
985
+
986
+ export default function Home() {
987
+ const [isLoading, setIsLoading] = useState(false);
988
+ const [currentStep, setCurrentStep] = useState('');
989
+ const [result, setResult] = useState(null);
990
+ const [error, setError] = useState(null);
991
+
992
+ const handleGenerate = useCallback(async (projectData) => {
993
+ setIsLoading(true);
994
+ setError(