create-solostack 1.2.1 → 1.2.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/generators/base.js +187 -174
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-solostack",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "The complete SaaS boilerplate for indie hackers - Next.js 15 with auth, payments, database, and email",
5
5
  "type": "module",
6
6
  "bin": {
@@ -316,206 +316,219 @@ export default function RootLayout({
316
316
 
317
317
  import { useState, useEffect } from 'react';
318
318
  import Link from 'next/link';
319
- import { Rocket, Users, DollarSign, Code, Database, CreditCard, Mail, Lock, CheckCircle2, Trophy, ArrowRight, Play, TrendingUp, Box } from 'lucide-react';
319
+ import {
320
+ CheckCircle2,
321
+ AlertCircle,
322
+ Terminal,
323
+ Database,
324
+ Shield,
325
+ CreditCard,
326
+ Mail,
327
+ Loader2,
328
+ ChevronRight,
329
+ ExternalLink,
330
+ Book
331
+ } from 'lucide-react';
320
332
 
321
333
  export default function Home() {
322
- const [mounted, setMounted] = useState(false);
323
- const [stats, setStats] = useState({
324
- mrr: 0,
325
- users: 0,
326
- shipped: 0
327
- });
334
+ const [status, setStatus] = useState<any>(null);
335
+ const [loading, setLoading] = useState(true);
328
336
 
329
337
  useEffect(() => {
330
- setMounted(true);
338
+ fetch('/api/setup')
339
+ .then(res => res.json())
340
+ .then(data => {
341
+ setStatus(data);
342
+ setLoading(false);
343
+ })
344
+ .catch(() => setLoading(false));
331
345
  }, []);
332
346
 
333
- const incrementStats = () => {
334
- setStats(prev => ({
335
- mrr: prev.mrr + 100,
336
- users: prev.users + 5,
337
- shipped: prev.shipped + 1
338
- }));
339
- };
340
-
341
- if (!mounted) return null;
342
-
343
347
  return (
344
348
  <div className="min-h-screen bg-zinc-950 text-zinc-50 font-sans selection:bg-indigo-500/30">
345
349
 
346
- {/* Navigation */}
347
- <nav className="fixed w-full z-50 border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-md">
348
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
349
- <div className="flex justify-between h-16 items-center">
350
- <div className="flex items-center gap-2">
351
- <div className="h-8 w-8 bg-indigo-600 rounded-lg flex items-center justify-center">
352
- <Rocket className="h-5 w-5 text-white" />
353
- </div>
354
- <span className="text-xl font-bold tracking-tight">${projectName}</span>
355
- </div>
356
- <div className="flex items-center gap-4">
357
- {process.env.NODE_ENV === 'development' && (
358
- <Link
359
- href="/setup"
360
- className="text-sm font-medium text-zinc-400 hover:text-white transition-colors flex items-center gap-2"
361
- >
362
- <CheckCircle2 className="h-4 w-4" />
363
- Diagnostics
364
- </Link>
365
- )}
366
- <Link
367
- href="/login"
368
- className="text-sm font-medium text-zinc-400 hover:text-white transition-colors"
369
- >
370
- Sign In
371
- </Link>
372
- <Link
373
- href="/signup"
374
- className="inline-flex items-center justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-zinc-950 shadow-sm hover:bg-zinc-200 transition-colors"
375
- >
376
- Get Started
377
- </Link>
378
- </div>
350
+ {/* Header */}
351
+ <header className="border-b border-zinc-900 bg-zinc-950/50 backdrop-blur-xl sticky top-0 z-10">
352
+ <div className="max-w-5xl mx-auto px-6 h-16 flex items-center justify-between">
353
+ <div className="font-bold text-lg tracking-tight flex items-center gap-2">
354
+ <div className="w-3 h-3 rounded-full bg-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.5)]" />
355
+ <span className="text-zinc-100">${projectName}</span>
379
356
  </div>
380
- </div>
381
- </nav>
382
-
383
- {/* Hero Section */}
384
- <div className="relative pt-32 pb-20 sm:pt-40 sm:pb-24 overflow-hidden">
385
-
386
- <div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center z-10">
387
- <div className="inline-flex items-center rounded-full border border-zinc-800 bg-zinc-900/50 px-3 py-1 text-sm text-zinc-400 mb-8 backdrop-blur-xl">
388
- <span className="flex h-2 w-2 rounded-full bg-emerald-500 mr-2 animate-pulse"></span>
389
- v1.2.1 is now live
357
+ <div className="flex items-center gap-4">
358
+ <span className="text-xs font-mono text-zinc-500 bg-zinc-900 px-2 py-1 rounded border border-zinc-800">
359
+ v0.1.0 (Dev Mode)
360
+ </span>
361
+ <a
362
+ href="https://github.com/danish296/create-solostack"
363
+ target="_blank"
364
+ rel="noreferrer"
365
+ className="text-zinc-400 hover:text-white transition-colors"
366
+ >
367
+ <ExternalLink className="w-4 h-4" />
368
+ </a>
390
369
  </div>
391
-
392
- <h1 className="text-5xl sm:text-7xl font-bold tracking-tight mb-8 bg-gradient-to-b from-white to-zinc-500 bg-clip-text text-transparent pb-2">
393
- Build your SaaS <br className="hidden sm:block" />
394
- <span className="text-white">in record time.</span>
370
+ </div>
371
+ </header>
372
+
373
+ <main className="max-w-5xl mx-auto px-6 py-12">
374
+ {/* Welcome Section */}
375
+ <div className="mb-12 border-b border-zinc-900 pb-12">
376
+ <h1 className="text-4xl font-bold tracking-tight mb-4 text-white">
377
+ Your Stack is Ready.
395
378
  </h1>
396
-
397
- <p className="mt-4 text-xl text-zinc-400 max-w-2xl mx-auto mb-10 leading-relaxed">
398
- The modern stack for ambitious developers. Authentication, payments, database, and emails — configured and ready to scale.
379
+ <p className="text-zinc-400 text-lg max-w-2xl leading-relaxed">
380
+ Welcome to your new SaaS application. This page is your
381
+ <span className="text-zinc-200 font-medium"> local development hub</span>.
382
+ Check your integrations, review the tech stack, and start building.
399
383
  </p>
400
-
401
- <div className="flex flex-col sm:flex-row items-center justify-center gap-4">
402
- <Link
403
- href="/signup"
404
- className="w-full sm:w-auto inline-flex items-center justify-center gap-2 rounded-lg bg-indigo-600 px-8 py-3.5 text-sm font-semibold text-white shadow-lg hover:bg-indigo-500 transition-all hover:scale-105 active:scale-95"
405
- >
406
- Start Building <ArrowRight className="h-4 w-4" />
407
- </Link>
408
- <a
409
- href="https://github.com/yourusername/create-solostack"
410
- target="_blank"
411
- rel="noopener noreferrer"
412
- className="w-full sm:w-auto inline-flex items-center justify-center gap-2 rounded-lg border border-zinc-800 bg-zinc-900/50 px-8 py-3.5 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:text-white transition-all backdrop-blur-sm"
413
- >
414
- Star on GitHub
415
- </a>
384
+
385
+ <div className="mt-8 flex items-center gap-4">
386
+ <div className="flex items-center gap-2 text-sm text-zinc-500 bg-zinc-900/50 px-3 py-2 rounded-md border border-zinc-800 font-mono">
387
+ <Terminal className="w-4 h-4 text-indigo-400" />
388
+ <span>src/app/page.tsx</span>
389
+ </div>
390
+ <span className="text-zinc-600 text-sm">← Edit this file to build your landing page</span>
416
391
  </div>
392
+ </div>
393
+
394
+ {/* System Status Grid */}
395
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
396
+ <StatusCard
397
+ title="Database"
398
+ icon={<Database className="w-5 h-5 text-emerald-400" />}
399
+ status={status?.database?.connected}
400
+ loading={loading}
401
+ description="PostgreSQL + Prisma"
402
+ details={status?.database?.connected ? \`User count: \${status.database.userCount}\` : status?.database?.error || "Not connected"}
403
+ actionLabel="Open Studio"
404
+ actionCommand="npm run db:studio"
405
+ />
406
+ <StatusCard
407
+ title="Authentication"
408
+ icon={<Shield className="w-5 h-5 text-blue-400" />}
409
+ status={status?.auth?.configured}
410
+ loading={loading}
411
+ description={status?.auth?.providers?.map((p: any) => p.name).join(', ') || "No providers"}
412
+ details={status?.auth?.configured ? "Ready to authenticate users" : "Missing secrets"}
413
+ actionLabel="Read Docs"
414
+ actionUrl="https://next-auth.js.org"
415
+ />
416
+ <StatusCard
417
+ title="Payments"
418
+ icon={<CreditCard className="w-5 h-5 text-purple-400" />}
419
+ status={status?.stripe?.configured}
420
+ loading={loading}
421
+ description="Stripe"
422
+ details={status?.stripe?.configured ? "API Keys present" : "Missing API Keys"}
423
+ actionLabel="Dashboard"
424
+ actionUrl="https://dashboard.stripe.com/test/apikeys"
425
+ />
426
+ <StatusCard
427
+ title="Email"
428
+ icon={<Mail className="w-5 h-5 text-pink-400" />}
429
+ status={status?.email?.configured}
430
+ loading={loading}
431
+ description="Resend"
432
+ details={status?.email?.configured ? "Ready to send" : "Missing API Key"}
433
+ actionLabel="Send Test"
434
+ actionUrl="/setup"
435
+ />
436
+ </div>
417
437
 
418
- {/* Interactive Game: SaaS Simulator */}
419
- <div className="mt-20 max-w-4xl mx-auto">
420
- <div className="relative rounded-xl border border-zinc-800 bg-zinc-900/50 p-2 backdrop-blur-xl shadow-2xl">
421
- <div className="absolute -inset-1 rounded-xl bg-gradient-to-r from-indigo-500/20 via-purple-500/20 to-pink-500/20 blur opacity-75"></div>
422
- <div className="relative rounded-lg bg-zinc-950 p-8">
423
- <div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
424
- <div className="p-4 rounded-lg bg-zinc-900/50 border border-zinc-800">
425
- <div className="flex items-center gap-2 text-zinc-400 mb-2">
426
- <TrendingUp className="h-4 w-4 text-emerald-500" /> MRR
427
- </div>
428
- <div className="text-3xl font-mono font-bold text-white">
429
- \${stats.mrr.toLocaleString()}
430
- </div>
431
- </div>
432
- <div className="p-4 rounded-lg bg-zinc-900/50 border border-zinc-800">
433
- <div className="flex items-center gap-2 text-zinc-400 mb-2">
434
- <Users className="h-4 w-4 text-blue-500" /> Users
435
- </div>
436
- <div className="text-3xl font-mono font-bold text-white">
437
- {stats.users.toLocaleString()}
438
- </div>
439
- </div>
440
- <div className="p-4 rounded-lg bg-zinc-900/50 border border-zinc-800">
441
- <div className="flex items-center gap-2 text-zinc-400 mb-2">
442
- <Box className="h-4 w-4 text-purple-500" /> Shipped
443
- </div>
444
- <div className="text-3xl font-mono font-bold text-white">
445
- {stats.shipped}
446
- </div>
447
- </div>
448
- </div>
449
-
450
- <div className="text-center">
451
- <p className="text-zinc-500 mb-6 font-mono text-sm">
452
- > CLICK_BELOW_TO_SIMULATE_GROWTH.exe
453
- </p>
454
- <button
455
- onClick={incrementStats}
456
- className="group relative inline-flex items-center justify-center rounded-full bg-zinc-100 px-8 py-4 font-bold text-zinc-950 transition-all hover:scale-105 active:scale-95 shadow-lg hover:shadow-indigo-500/20"
457
- >
458
- <Play className="mr-2 h-5 w-5 fill-zinc-950 transition-transform group-hover:translate-x-1" />
459
- SHIP FEATURE
460
- </button>
461
- <p className="mt-4 text-xs text-zinc-600">
462
- * Results may vary. Consistency is key.
463
- </p>
464
- </div>
465
- </div>
466
- </div>
438
+ {/* Resources Section */}
439
+ <div>
440
+ <h2 className="text-xl font-semibold mb-6 flex items-center gap-2">
441
+ <Book className="w-5 h-5 text-zinc-400" />
442
+ Documentation & Resources
443
+ </h2>
444
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
445
+ <ResourceLink
446
+ href="https://nextjs.org/docs"
447
+ title="Next.js 15"
448
+ desc="App Router, Server Actions"
449
+ />
450
+ <ResourceLink
451
+ href="https://ui.shadcn.com"
452
+ title="shadcn/ui"
453
+ desc="Component Library"
454
+ />
455
+ <ResourceLink
456
+ href="https://www.prisma.io/docs"
457
+ title="Prisma"
458
+ desc="Database ORM"
459
+ />
467
460
  </div>
468
461
  </div>
469
462
 
470
- {/* Background Gradients */}
471
- <div className="absolute top-0 left-0 right-0 h-[500px] bg-[radial-gradient(circle_at_50%_0%,rgba(99,102,241,0.15),transparent_70%)] pointer-events-none"></div>
472
- </div>
463
+ </main>
464
+ </div>
465
+ );
466
+ }
473
467
 
474
- {/* Features Grid */}
475
- <div className="py-24 border-t border-zinc-900 bg-zinc-950">
476
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
477
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
478
- <FeatureCard
479
- icon={<Lock className="h-6 w-6 text-indigo-400" />}
480
- title="Authentication"
481
- description="Secure user management with NextAuth or Supabase. Login, signup, and protected routes."
482
- />
483
- <FeatureCard
484
- icon={<Database className="h-6 w-6 text-emerald-400" />}
485
- title="Database"
486
- description="Type-safe data access with Prisma and PostgreSQL. Seeding scripts included."
487
- />
488
- <FeatureCard
489
- icon={<CreditCard className="h-6 w-6 text-purple-400" />}
490
- title="Payments"
491
- description="Stripe integration with checkout sessions, webhooks, and subscription management."
492
- />
493
- <FeatureCard
494
- icon={<Mail className="h-6 w-6 text-pink-400" />}
495
- title="Emails"
496
- description="Transactional emails with Resend and React Email. Beautiful templates out of the box."
497
- />
498
- </div>
468
+ function StatusCard({ title, icon, status, loading, description, details, actionLabel, actionCommand, actionUrl }: any) {
469
+ return (
470
+ <div className="border border-zinc-800 bg-zinc-900/30 rounded-xl p-6 hover:bg-zinc-900/50 transition-colors">
471
+ <div className="flex justify-between items-start mb-4">
472
+ <div className="p-2 bg-zinc-950 rounded-lg border border-zinc-800">
473
+ {icon}
499
474
  </div>
475
+ {loading ? (
476
+ <Loader2 className="w-5 h-5 animate-spin text-zinc-600" />
477
+ ) : status ? (
478
+ <CheckCircle2 className="w-5 h-5 text-emerald-500" />
479
+ ) : (
480
+ <AlertCircle className="w-5 h-5 text-amber-500" />
481
+ )}
482
+ </div>
483
+
484
+ <h3 className="text-lg font-medium text-white mb-1">{title}</h3>
485
+ <p className="text-sm font-medium text-zinc-300 mb-2">{description}</p>
486
+
487
+ <div className="min-h-[20px] mb-6">
488
+ <p className={\`text-xs \${status ? 'text-zinc-500' : 'text-amber-500/80'}\`}>
489
+ {details}
490
+ </p>
500
491
  </div>
501
492
 
502
- {/* Footer */}
503
- <footer className="border-t border-zinc-900 bg-zinc-950 py-12">
504
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-6">
505
- <div className="flex items-center gap-2">
506
- <div className="h-6 w-6 bg-zinc-800 rounded flex items-center justify-center">
507
- <Rocket className="h-3 w-3 text-zinc-400" />
508
- </div>
509
- <span className="text-sm font-semibold text-zinc-300">${projectName}</span>
510
- </div>
511
- <p className="text-sm text-zinc-500">
512
- Built with create-solostack.
513
- </p>
493
+ {actionCommand ? (
494
+ <div className="flex items-center justify-between pt-4 border-t border-zinc-800/50">
495
+ <code className="text-xs bg-black px-2 py-1 rounded text-zinc-400 font-mono">
496
+ {actionCommand}
497
+ </code>
498
+ </div>
499
+ ) : actionUrl ? (
500
+ <div className="pt-4 border-t border-zinc-800/50">
501
+ {actionUrl.startsWith('http') ? (
502
+ <a href={actionUrl} target="_blank" rel="noreferrer" className="text-xs font-medium text-indigo-400 hover:text-indigo-300 flex items-center gap-1">
503
+ {actionLabel} <ExternalLink className="w-3 h-3" />
504
+ </a>
505
+ ) : (
506
+ <Link href={actionUrl} className="text-xs font-medium text-indigo-400 hover:text-indigo-300 flex items-center gap-1">
507
+ {actionLabel} <ChevronRight className="w-3 h-3" />
508
+ </Link>
509
+ )}
514
510
  </div>
515
- </footer>
511
+ ) : null}
516
512
  </div>
517
513
  );
518
514
  }
515
+
516
+ function ResourceLink({ href, title, desc }: any) {
517
+ return (
518
+ <a
519
+ href={href}
520
+ target="_blank"
521
+ rel="noreferrer"
522
+ className="group block p-4 rounded-lg border border-zinc-800 bg-zinc-900/20 hover:bg-zinc-800/50 transition-all"
523
+ >
524
+ <div className="flex items-center justify-between mb-1">
525
+ <span className="font-medium text-zinc-200 group-hover:text-white transition-colors">{title}</span>
526
+ <ExternalLink className="w-3 h-3 text-zinc-600 group-hover:text-zinc-400" />
527
+ </div>
528
+ <p className="text-xs text-zinc-500">{desc}</p>
529
+ </a>
530
+ );
531
+ }
519
532
  `;
520
533
 
521
534
  await writeFile(path.join(projectPath, 'src/app/page.tsx'), pageTsx);