@webjsdev/cli 0.10.37 → 0.10.38

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.
@@ -118,6 +118,10 @@ export async function writeSaasFiles(appDir, opts = {}) {
118
118
  " ...(process.env.AUTH_GOOGLE_ID ? [Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET })] : []),",
119
119
  " ],",
120
120
  " secret: authSecret,",
121
+ " // A failed credentials sign-in 302s to `${pages.error}?error=CredentialsSignin`.",
122
+ " // Point it at /login so app/login/page.ts reads searchParams.error and shows a",
123
+ " // message, instead of the createAuth default (the home page) swallowing the error.",
124
+ " pages: { error: '/login' },",
121
125
  "});",
122
126
  "",
123
127
  ].join('\n'));
@@ -339,7 +343,17 @@ export async function writeSaasFiles(appDir, opts = {}) {
339
343
  "",
340
344
  "export const metadata = { title: 'Login' };",
341
345
  "",
342
- "export default function LoginPage() {",
346
+ "// A failed sign-in 302s back here with ?error=... (createAuth is configured",
347
+ "// with pages.error: '/login' in lib/auth.server.ts). Map the code to a plain",
348
+ "// message so a bad password gets visible feedback instead of a silent bounce.",
349
+ "function errorMessage(code: string | undefined): string | null {",
350
+ " if (!code) return null;",
351
+ " if (code === 'CredentialsSignin') return 'Invalid email or password.';",
352
+ " return 'Could not sign you in. Please try again.';",
353
+ "}",
354
+ "",
355
+ "export default function LoginPage({ searchParams }: { searchParams: { error?: string } }) {",
356
+ " const error = errorMessage(searchParams.error);",
343
357
  " return html`",
344
358
  " <div class=\"max-w-sm mx-auto mt-12\">",
345
359
  " <div class=${cardClass()}>",
@@ -348,6 +362,7 @@ export async function writeSaasFiles(appDir, opts = {}) {
348
362
  " <p class=${cardDescriptionClass()}>Welcome back: log in to continue.</p>",
349
363
  " </div>",
350
364
  " <div class=${cardContentClass()}>",
365
+ " ${error ? html`<p role=\"alert\" class=\"mb-4 text-sm text-destructive\">${error}</p>` : ''}",
351
366
  " <form method=\"POST\" action=\"/api/auth/signin/credentials\" class=\"flex flex-col gap-4\">",
352
367
  " <!-- createAuth reads redirectTo from the posted form and 302s there after a successful signin. -->",
353
368
  " <input type=\"hidden\" name=\"redirectTo\" value=\"/dashboard\">",
@@ -461,12 +476,39 @@ export async function writeSaasFiles(appDir, opts = {}) {
461
476
  "",
462
477
  ].join('\n'));
463
478
 
479
+ // app/dashboard/layout.ts: a thin sub-nav shared by every /dashboard page
480
+ // (page.ts and settings/page.ts). It carries the logout control so a signed-in
481
+ // user can end the session from anywhere under /dashboard.
482
+ await writeFile(join(appDir, 'app', 'dashboard', 'layout.ts'), [
483
+ "import { html } from '@webjsdev/core';",
484
+ "import { buttonClass } from '#components/ui/button.ts';",
485
+ "",
486
+ "// Nested layout for the protected /dashboard subtree. Logout is a plain",
487
+ "// <form method=\"POST\"> posting to the createAuth signout route: it clears the",
488
+ "// session cookie and 302s home, and works with JS off (progressive-enhancement",
489
+ "// default). signOut is server-only (lib/auth.server.ts), so we POST to its route",
490
+ "// rather than import it into a browser-shipping page. After signout the dashboard",
491
+ "// middleware bounces any later /dashboard visit to /login.",
492
+ "export default function DashboardLayout({ children }: { children: unknown }) {",
493
+ " return html`",
494
+ " <nav class=\"flex items-center gap-4 mb-6 pb-4 border-b border-border\">",
495
+ " <a href=\"/dashboard\" class=\"text-sm font-medium hover:underline\">Dashboard</a>",
496
+ " <a href=\"/dashboard/settings\" class=\"text-sm font-medium hover:underline\">Settings</a>",
497
+ " <form method=\"POST\" action=\"/api/auth/signout\" class=\"ml-auto\">",
498
+ " <button class=${buttonClass({ variant: 'outline', size: 'sm' })} type=\"submit\">Log out</button>",
499
+ " </form>",
500
+ " </nav>",
501
+ " ${children}",
502
+ " `;",
503
+ "}",
504
+ "",
505
+ ].join('\n'));
506
+
464
507
  // app/dashboard/page.ts
465
508
  await writeFile(join(appDir, 'app', 'dashboard', 'page.ts'), [
466
509
  "import { html } from '@webjsdev/core';",
467
510
  "import { currentUser } from '#modules/auth/queries/current-user.server.ts';",
468
- "import { cardClass, cardHeaderClass, cardTitleClass, cardDescriptionClass, cardContentClass } from '#components/ui/card.ts';",
469
- "import { buttonClass } from '#components/ui/button.ts';",
511
+ "import { cardClass, cardHeaderClass, cardTitleClass, cardDescriptionClass } from '#components/ui/card.ts';",
470
512
  "import { badgeClass } from '#components/ui/badge.ts';",
471
513
  "",
472
514
  "export const metadata = { title: 'Dashboard' };",
@@ -483,9 +525,6 @@ export async function writeSaasFiles(appDir, opts = {}) {
483
525
  " <h2 class=${cardTitleClass()}>Welcome, ${user?.name || user?.email}!</h2>",
484
526
  " <p class=${cardDescriptionClass()}>You're authenticated. Replace this scaffold with your real app.</p>",
485
527
  " </div>",
486
- " <div class=${cardContentClass()}>",
487
- " <a class=${buttonClass({ variant: 'outline' })} href=\"/dashboard/settings\">Settings</a>",
488
- " </div>",
489
528
  " </div>",
490
529
  " `;",
491
530
  "}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/cli",
3
- "version": "0.10.37",
3
+ "version": "0.10.38",
4
4
  "type": "module",
5
5
  "description": "webjs CLI - dev, start, create, db",
6
6
  "bin": {