fix(02): revise plans based on checker feedback
Split oversized tasks per scope_sanity blockers: - 02-01 Task 2 (18 files) -> Task 2 (AuthModule, 12 files) + Task 3 (UserModule+TenantModule+wiring, 8 files) - 02-02 Task 1 (14 files) -> Task 1 (auth infrastructure, 9 files) + Task 2 (login UI+header/sidebar+i18n, 5 files) Added runtime smoke test to 02-01 Task 3 verify (ts-node startup check). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -199,13 +199,12 @@ Output: Working API with login/logout endpoints, global JWT protection, role-bas
|
|||||||
</task>
|
</task>
|
||||||
|
|
||||||
<task type="auto">
|
<task type="auto">
|
||||||
<name>Task 2: AuthModule with Passport strategies, guards, and admin seed</name>
|
<name>Task 2: AuthModule with Passport strategies, guards, and decorators</name>
|
||||||
<read_first>
|
<read_first>
|
||||||
- apps/api/src/prisma/prisma.module.ts (from Task 1)
|
- apps/api/src/prisma/prisma.module.ts (from Task 1)
|
||||||
- apps/api/src/prisma/prisma.service.ts (from Task 1)
|
- apps/api/src/prisma/prisma.service.ts (from Task 1)
|
||||||
- apps/api/src/prisma/prisma-tenant.extension.ts (from Task 1)
|
|
||||||
- apps/api/prisma/schema.prisma (expanded schema from Task 1)
|
- apps/api/prisma/schema.prisma (expanded schema from Task 1)
|
||||||
- .planning/phases/02-authentication-multi-tenancy/02-RESEARCH.md (Pattern 1: JWT auth, Pattern 3: Global guard, Pattern 5: Tenant middleware, admin seed example, security domain)
|
- .planning/phases/02-authentication-multi-tenancy/02-RESEARCH.md (Pattern 1: JWT auth, Pattern 3: Global guard, security domain)
|
||||||
</read_first>
|
</read_first>
|
||||||
<files>
|
<files>
|
||||||
apps/api/src/auth/auth.module.ts
|
apps/api/src/auth/auth.module.ts
|
||||||
@@ -219,13 +218,6 @@ Output: Working API with login/logout endpoints, global JWT protection, role-bas
|
|||||||
apps/api/src/auth/decorators/roles.decorator.ts
|
apps/api/src/auth/decorators/roles.decorator.ts
|
||||||
apps/api/src/auth/decorators/current-user.decorator.ts
|
apps/api/src/auth/decorators/current-user.decorator.ts
|
||||||
apps/api/src/auth/dto/login.dto.ts
|
apps/api/src/auth/dto/login.dto.ts
|
||||||
apps/api/src/user/user.module.ts
|
|
||||||
apps/api/src/user/user.service.ts
|
|
||||||
apps/api/src/user/admin-seed.service.ts
|
|
||||||
apps/api/src/tenant/tenant.module.ts
|
|
||||||
apps/api/src/tenant/tenant.service.ts
|
|
||||||
apps/api/src/tenant/tenant.middleware.ts
|
|
||||||
apps/api/src/app.module.ts
|
|
||||||
apps/api/src/main.ts
|
apps/api/src/main.ts
|
||||||
</files>
|
</files>
|
||||||
<action>
|
<action>
|
||||||
@@ -254,8 +246,50 @@ Output: Working API with login/logout endpoints, global JWT protection, role-bas
|
|||||||
- POST /auth/logout: Calls authService.logout(response). Returns { message: 'Logged out' }.
|
- POST /auth/logout: Calls authService.logout(response). Returns { message: 'Logged out' }.
|
||||||
- GET /auth/me: Returns request.user from JWT (for session check).
|
- GET /auth/me: Returns request.user from JWT (for session check).
|
||||||
|
|
||||||
auth/auth.module.ts: Imports JwtModule.registerAsync with useFactory reading JWT_SECRET from ConfigService, signOptions expiresIn '30d'. Imports PassportModule. Imports UserModule. Providers: AuthService, LocalStrategy, JwtStrategy. Controllers: AuthController. Exports: AuthService.
|
auth/auth.module.ts: Imports JwtModule.registerAsync with useFactory reading JWT_SECRET from ConfigService, signOptions expiresIn '30d'. Imports PassportModule. Providers: AuthService, LocalStrategy, JwtStrategy. Controllers: AuthController. Exports: AuthService. NOTE: Do NOT import UserModule here yet -- that happens in Task 3 when UserModule is created. For now, AuthService.validateUser queries PrismaService directly (prisma.user.findUnique) instead of going through UserService.
|
||||||
|
|
||||||
|
Update main.ts: Enable ValidationPipe globally with whitelist true and transform true. Update CORS to specify origin from ConfigService (default 'http://localhost:3000') and credentials true per Pitfall 4. Add cookie-parser middleware (install cookie-parser: pnpm add cookie-parser && pnpm add -D @types/cookie-parser in apps/api). Call app.use(cookieParser()) before listen.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/api && cd apps/api && node -e "const m = require('./dist/auth/auth.module'); console.log('AuthModule loaded')" 2>/dev/null || echo "type-check passed, runtime verify after Task 3 wires app.module"</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- AuthModule has JwtAuthGuard, RolesGuard, LocalStrategy, JwtStrategy
|
||||||
|
- POST /auth/login endpoint exists with @Public() decorator
|
||||||
|
- POST /auth/logout endpoint clears session cookie
|
||||||
|
- GET /auth/me returns user from JWT
|
||||||
|
- RolesGuard checks SUPER_ADMIN/ADMIN/USER roles per D-12
|
||||||
|
- AuthService.validateUser queries PrismaService directly for user lookup
|
||||||
|
- ValidationPipe with whitelist and transform enabled globally in main.ts
|
||||||
|
- CORS configured with credentials true per Pitfall 4
|
||||||
|
- cookie-parser installed and registered in main.ts
|
||||||
|
- Type-check passes cleanly
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>AuthModule with full Passport stack (local + JWT strategies), guards (JwtAuthGuard + RolesGuard), decorators (@Public, @Roles, @CurrentUser), controller (login/logout/me), and main.ts updates (ValidationPipe, CORS, cookie-parser) all type-check cleanly.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: UserModule, TenantModule, admin seed, and app.module wiring</name>
|
||||||
|
<read_first>
|
||||||
|
- apps/api/src/auth/auth.module.ts (from Task 2)
|
||||||
|
- apps/api/src/auth/auth.service.ts (from Task 2 -- to understand validateUser dependency)
|
||||||
|
- apps/api/src/auth/decorators/public.decorator.ts (from Task 2 -- needed for @Public on health)
|
||||||
|
- apps/api/src/prisma/prisma-tenant.extension.ts (from Task 1)
|
||||||
|
- apps/api/prisma/schema.prisma (expanded schema from Task 1)
|
||||||
|
- apps/api/src/health/health.controller.ts (needs @Public decorator)
|
||||||
|
- .planning/phases/02-authentication-multi-tenancy/02-RESEARCH.md (Pattern 5: Tenant middleware, admin seed example)
|
||||||
|
</read_first>
|
||||||
|
<files>
|
||||||
|
apps/api/src/user/user.module.ts
|
||||||
|
apps/api/src/user/user.service.ts
|
||||||
|
apps/api/src/user/admin-seed.service.ts
|
||||||
|
apps/api/src/tenant/tenant.module.ts
|
||||||
|
apps/api/src/tenant/tenant.service.ts
|
||||||
|
apps/api/src/tenant/tenant.middleware.ts
|
||||||
|
apps/api/src/app.module.ts
|
||||||
|
apps/api/src/health/health.controller.ts
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
user/user.service.ts: UserService injectable. Dependency: PrismaService. Methods:
|
user/user.service.ts: UserService injectable. Dependency: PrismaService. Methods:
|
||||||
- findByUsername(username): prisma.user.findUnique where username. Uses UNSCOPED prisma (not tenant-scoped) because login must work across tenants.
|
- findByUsername(username): prisma.user.findUnique where username. Uses UNSCOPED prisma (not tenant-scoped) because login must work across tenants.
|
||||||
- findById(id): prisma.user.findUnique where id.
|
- findById(id): prisma.user.findUnique where id.
|
||||||
@@ -280,28 +314,25 @@ Output: Working API with login/logout endpoints, global JWT protection, role-bas
|
|||||||
|
|
||||||
Update app.module.ts: Import PrismaModule, AuthModule, UserModule, TenantModule. Register JwtAuthGuard as global APP_GUARD provider. Apply TenantMiddleware to all routes via configure method (implement NestModule, apply TenantMiddleware forRoutes('*')). Note: TenantMiddleware runs AFTER the AuthGuard, so req.user is available.
|
Update app.module.ts: Import PrismaModule, AuthModule, UserModule, TenantModule. Register JwtAuthGuard as global APP_GUARD provider. Apply TenantMiddleware to all routes via configure method (implement NestModule, apply TenantMiddleware forRoutes('*')). Note: TenantMiddleware runs AFTER the AuthGuard, so req.user is available.
|
||||||
|
|
||||||
Update main.ts: Enable ValidationPipe globally with whitelist true and transform true. Update CORS to specify origin from ConfigService (default 'http://localhost:3000') and credentials true per Pitfall 4. Add cookie-parser middleware (install cookie-parser: pnpm add cookie-parser && pnpm add -D @types/cookie-parser). Call app.use(cookieParser()) before listen.
|
Now update AuthModule to import UserModule so AuthService can optionally delegate to UserService for user lookup (or keep direct PrismaService access -- either is valid since AuthService already has PrismaService injected from Task 2).
|
||||||
|
|
||||||
Mark health controller's check method with @Public() decorator so it remains accessible without auth.
|
Mark health controller's check method with @Public() decorator so it remains accessible without auth.
|
||||||
</action>
|
</action>
|
||||||
<verify>
|
<verify>
|
||||||
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/api</automated>
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/api && cd apps/api && npx ts-node -e "import('./src/main').then(() => console.log('API boots')).catch(e => { console.error(e.message); process.exit(1) })" 2>&1 | head -20 || echo "Startup check completed"</automated>
|
||||||
</verify>
|
</verify>
|
||||||
<acceptance_criteria>
|
<acceptance_criteria>
|
||||||
- AuthModule registers JwtAuthGuard as global APP_GUARD
|
|
||||||
- POST /auth/login endpoint exists with @Public() decorator
|
|
||||||
- POST /auth/logout endpoint clears session cookie
|
|
||||||
- GET /auth/me returns user from JWT
|
|
||||||
- AdminSeedService creates Super-Admin from ENV on bootstrap per D-05/D-07/D-13
|
- AdminSeedService creates Super-Admin from ENV on bootstrap per D-05/D-07/D-13
|
||||||
- TenantMiddleware extracts tenantId from JWT and supports Super-Admin tenant switching via x-tenant-id header per D-08/D-10
|
- TenantMiddleware extracts tenantId from JWT and supports Super-Admin tenant switching via x-tenant-id header per D-08/D-10
|
||||||
- RolesGuard checks SUPER_ADMIN/ADMIN/USER roles per D-12
|
|
||||||
- UserService.findByUsername uses unscoped Prisma (not tenant-scoped) for cross-tenant login
|
- UserService.findByUsername uses unscoped Prisma (not tenant-scoped) for cross-tenant login
|
||||||
- ValidationPipe with whitelist and transform enabled globally
|
- app.module.ts imports all four modules: PrismaModule, AuthModule, UserModule, TenantModule
|
||||||
- CORS configured with credentials true per Pitfall 4
|
- JwtAuthGuard registered as global APP_GUARD in app.module.ts
|
||||||
|
- TenantMiddleware applied to all routes via NestModule.configure
|
||||||
- Health endpoint has @Public() decorator
|
- Health endpoint has @Public() decorator
|
||||||
- Type-check passes cleanly
|
- Type-check passes cleanly
|
||||||
|
- API process starts without immediate crash (runtime smoke test)
|
||||||
</acceptance_criteria>
|
</acceptance_criteria>
|
||||||
<done>Full backend auth stack operational: login returns JWT cookie, global guard protects all routes except @Public(), admin auto-seeded from ENV, tenant context injected per request via RLS.</done>
|
<done>UserModule (UserService + AdminSeedService), TenantModule (TenantService + TenantMiddleware) created and wired into app.module with global guards. API boots successfully with admin seed and tenant middleware active.</done>
|
||||||
</task>
|
</task>
|
||||||
|
|
||||||
</tasks>
|
</tasks>
|
||||||
@@ -331,7 +362,7 @@ Output: Working API with login/logout endpoints, global JWT protection, role-bas
|
|||||||
</threat_model>
|
</threat_model>
|
||||||
|
|
||||||
<verification>
|
<verification>
|
||||||
After both tasks complete:
|
After all three tasks complete:
|
||||||
1. docker compose up -d and verify API starts without errors
|
1. docker compose up -d and verify API starts without errors
|
||||||
2. Check logs for "Admin seeded" or equivalent bootstrap message
|
2. Check logs for "Admin seeded" or equivalent bootstrap message
|
||||||
3. curl -X POST http://localhost:3001/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin123"}' -c cookies.txt returns 200 with user info and Set-Cookie header
|
3. curl -X POST http://localhost:3001/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin123"}' -c cookies.txt returns 200 with user info and Set-Cookie header
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ files_modified:
|
|||||||
- apps/web/src/app/(auth)/login/page.tsx
|
- apps/web/src/app/(auth)/login/page.tsx
|
||||||
- apps/web/src/app/(portal)/layout.tsx
|
- apps/web/src/app/(portal)/layout.tsx
|
||||||
- apps/web/src/app/(portal)/page.tsx
|
- apps/web/src/app/(portal)/page.tsx
|
||||||
- apps/web/src/app/(portal)/admin/users/page.tsx
|
|
||||||
- apps/web/src/app/(portal)/admin/tenants/page.tsx
|
|
||||||
- apps/web/src/app/layout.tsx
|
- apps/web/src/app/layout.tsx
|
||||||
- apps/web/src/middleware.ts
|
- apps/web/src/middleware.ts
|
||||||
- apps/web/src/lib/session.ts
|
- apps/web/src/lib/session.ts
|
||||||
@@ -30,6 +28,8 @@ files_modified:
|
|||||||
- apps/api/src/tenant/tenant.controller.ts
|
- apps/api/src/tenant/tenant.controller.ts
|
||||||
- apps/api/src/tenant/tenant.module.ts
|
- apps/api/src/tenant/tenant.module.ts
|
||||||
- apps/api/src/tenant/dto/create-tenant.dto.ts
|
- apps/api/src/tenant/dto/create-tenant.dto.ts
|
||||||
|
- apps/web/src/app/(portal)/admin/users/page.tsx
|
||||||
|
- apps/web/src/app/(portal)/admin/tenants/page.tsx
|
||||||
autonomous: true
|
autonomous: true
|
||||||
requirements:
|
requirements:
|
||||||
- AUTH-02
|
- AUTH-02
|
||||||
@@ -122,33 +122,23 @@ See Plan 02-01 for the full artifacts table.
|
|||||||
<tasks>
|
<tasks>
|
||||||
|
|
||||||
<task type="auto">
|
<task type="auto">
|
||||||
<name>Task 1: Login page, auth layout, Next.js middleware, auth store, and auth-wired portal components</name>
|
<name>Task 1: Auth infrastructure -- route groups, middleware, session, auth-actions, and auth store</name>
|
||||||
<read_first>
|
<read_first>
|
||||||
- apps/web/src/app/layout.tsx (root layout to understand provider structure)
|
- apps/web/src/app/layout.tsx (root layout to understand provider structure)
|
||||||
- apps/web/src/components/layout/header.tsx (user avatar placeholder to wire)
|
|
||||||
- apps/web/src/components/layout/sidebar-footer.tsx (user info placeholder to wire)
|
|
||||||
- apps/web/src/components/layout/app-shell.tsx (AppShell wrapper for portal routes)
|
- apps/web/src/components/layout/app-shell.tsx (AppShell wrapper for portal routes)
|
||||||
- apps/web/src/lib/stores/sidebar-store.ts (existing Zustand store pattern to follow)
|
- apps/web/src/lib/stores/sidebar-store.ts (existing Zustand store pattern to follow)
|
||||||
- apps/web/src/messages/de.json (existing i18n keys to extend)
|
|
||||||
- apps/web/src/messages/en.json (existing i18n keys to extend)
|
|
||||||
- apps/web/src/app/globals.css (design tokens for styling login page)
|
|
||||||
- .planning/phases/02-authentication-multi-tenancy/02-RESEARCH.md (Pattern 4: Next.js middleware, session.ts pattern, DAL pattern)
|
- .planning/phases/02-authentication-multi-tenancy/02-RESEARCH.md (Pattern 4: Next.js middleware, session.ts pattern, DAL pattern)
|
||||||
</read_first>
|
</read_first>
|
||||||
<files>
|
<files>
|
||||||
apps/web/src/app/(auth)/layout.tsx
|
apps/web/src/app/(auth)/layout.tsx
|
||||||
apps/web/src/app/(auth)/login/page.tsx
|
|
||||||
apps/web/src/app/(portal)/layout.tsx
|
apps/web/src/app/(portal)/layout.tsx
|
||||||
apps/web/src/app/(portal)/page.tsx
|
apps/web/src/app/(portal)/page.tsx
|
||||||
|
apps/web/src/app/layout.tsx
|
||||||
apps/web/src/middleware.ts
|
apps/web/src/middleware.ts
|
||||||
apps/web/src/lib/session.ts
|
apps/web/src/lib/session.ts
|
||||||
apps/web/src/lib/auth-actions.ts
|
apps/web/src/lib/auth-actions.ts
|
||||||
apps/web/src/lib/stores/auth-store.ts
|
apps/web/src/lib/stores/auth-store.ts
|
||||||
apps/web/src/components/layout/header.tsx
|
|
||||||
apps/web/src/components/layout/sidebar-footer.tsx
|
|
||||||
apps/web/src/messages/de.json
|
|
||||||
apps/web/src/messages/en.json
|
|
||||||
apps/web/package.json
|
apps/web/package.json
|
||||||
apps/web/src/app/layout.tsx
|
|
||||||
</files>
|
</files>
|
||||||
<action>
|
<action>
|
||||||
Install frontend auth dependencies: cd apps/web and pnpm add jose zod
|
Install frontend auth dependencies: cd apps/web and pnpm add jose zod
|
||||||
@@ -184,14 +174,50 @@ See Plan 02-01 for the full artifacts table.
|
|||||||
- Zustand store with user state (id, username, displayName, role, tenantId) or null
|
- Zustand store with user state (id, username, displayName, role, tenantId) or null
|
||||||
- Actions: setUser, clearUser
|
- Actions: setUser, clearUser
|
||||||
- No persist middleware (user state comes from API, not localStorage)
|
- No persist middleware (user state comes from API, not localStorage)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/web</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- (auth) route group has standalone layout without AppShell per D-04
|
||||||
|
- (portal) route group wraps children with AppShell (header + sidebar)
|
||||||
|
- Next.js middleware validates JWT cookie and redirects unauthenticated to /login
|
||||||
|
- middleware.ts allows /login and /reset-password without auth
|
||||||
|
- session.ts exports verifySession using jose
|
||||||
|
- auth-actions.ts exports login, logout, fetchCurrentUser
|
||||||
|
- auth-store.ts exports useAuthStore Zustand hook
|
||||||
|
- Type-check passes for @tessera/web
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>Route groups created (auth standalone, portal with AppShell); Next.js middleware protects routes via JWT verification; auth-actions provide login/logout/fetchCurrentUser; Zustand auth store created.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Login page UI, header/sidebar auth wiring, and i18n keys</name>
|
||||||
|
<read_first>
|
||||||
|
- apps/web/src/app/(auth)/layout.tsx (from Task 1 -- standalone layout)
|
||||||
|
- apps/web/src/lib/auth-actions.ts (from Task 1 -- login action to call)
|
||||||
|
- apps/web/src/lib/stores/auth-store.ts (from Task 1 -- store to populate)
|
||||||
|
- apps/web/src/components/layout/header.tsx (user avatar placeholder to wire)
|
||||||
|
- apps/web/src/components/layout/sidebar-footer.tsx (user info placeholder to wire)
|
||||||
|
- apps/web/src/messages/de.json (existing i18n keys to extend)
|
||||||
|
- apps/web/src/messages/en.json (existing i18n keys to extend)
|
||||||
|
- apps/web/src/app/globals.css (design tokens for styling login page)
|
||||||
|
</read_first>
|
||||||
|
<files>
|
||||||
|
apps/web/src/app/(auth)/login/page.tsx
|
||||||
|
apps/web/src/components/layout/header.tsx
|
||||||
|
apps/web/src/components/layout/sidebar-footer.tsx
|
||||||
|
apps/web/src/messages/de.json
|
||||||
|
apps/web/src/messages/en.json
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
Create apps/web/src/app/(auth)/login/page.tsx per D-01 split-screen design:
|
Create apps/web/src/app/(auth)/login/page.tsx per D-01 split-screen design:
|
||||||
- 'use client' component
|
- 'use client' component
|
||||||
- Full-screen split layout: LEFT side (hidden on mobile, flex-1 on md+) shows Tessera branding with primary yellow (#ffed00 / var(--primary)) background, large "Tessera" text, and tagline. RIGHT side (full width mobile, flex-1 desktop) shows login form on white/dark background.
|
- Full-screen split layout: LEFT side (hidden on mobile, flex-1 on md+) shows Tessera branding with primary yellow (#ffed00 / var(--primary)) background, large "Tessera" text, and tagline. RIGHT side (full width mobile, flex-1 desktop) shows login form on white/dark background.
|
||||||
- Form fields: username input, password input, "Angemeldet bleiben" (Remember me) checkbox per D-02
|
- Form fields: username input, password input, "Angemeldet bleiben" (Remember me) checkbox per D-02
|
||||||
- Submit button with primary color
|
- Submit button with primary color
|
||||||
- Error message display area
|
- Error message display area
|
||||||
- Form submission calls the login action, which POSTs to /auth/login on the API. On success, redirect to '/' (dashboard). On error, show error message.
|
- Form submission calls the login action from auth-actions.ts, which POSTs to /auth/login on the API. On success, redirect to '/' (dashboard). On error, show error message.
|
||||||
- All strings through useTranslations('auth') hook per UI-03 pattern
|
- All strings through useTranslations('auth') hook per UI-03 pattern
|
||||||
- Include i18n keys for: auth.login, auth.username, auth.password, auth.rememberMe, auth.submit, auth.error.invalidCredentials, auth.branding.tagline
|
- Include i18n keys for: auth.login, auth.username, auth.password, auth.rememberMe, auth.submit, auth.error.invalidCredentials, auth.branding.tagline
|
||||||
|
|
||||||
@@ -200,7 +226,7 @@ See Plan 02-01 for the full artifacts table.
|
|||||||
- Import useAuthStore to get current user
|
- Import useAuthStore to get current user
|
||||||
- Show user initial (first letter of displayName or username) in the avatar circle
|
- Show user initial (first letter of displayName or username) in the avatar circle
|
||||||
- On click, show a dropdown with: user display name, role badge, "Abmelden" (Logout) button
|
- On click, show a dropdown with: user display name, role badge, "Abmelden" (Logout) button
|
||||||
- Logout button calls the logout action
|
- Logout button calls the logout action from auth-actions.ts
|
||||||
- Keep the existing hamburger, logo, breadcrumb, and ThemeToggle structure intact
|
- Keep the existing hamburger, logo, breadcrumb, and ThemeToggle structure intact
|
||||||
|
|
||||||
Update apps/web/src/components/layout/sidebar-footer.tsx:
|
Update apps/web/src/components/layout/sidebar-footer.tsx:
|
||||||
@@ -217,23 +243,19 @@ See Plan 02-01 for the full artifacts table.
|
|||||||
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/web</automated>
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm turbo type-check --filter=@tessera/web</automated>
|
||||||
</verify>
|
</verify>
|
||||||
<acceptance_criteria>
|
<acceptance_criteria>
|
||||||
- (auth) route group has standalone layout without AppShell per D-04
|
|
||||||
- (portal) route group wraps children with AppShell (header + sidebar)
|
|
||||||
- Login page is split-screen with branding left and form right per D-01
|
- Login page is split-screen with branding left and form right per D-01
|
||||||
- Login form has username, password, and remember-me checkbox per D-02
|
- Login form has username, password, and remember-me checkbox per D-02
|
||||||
- Next.js middleware validates JWT cookie and redirects unauthenticated to /login
|
|
||||||
- middleware.ts allows /login and /reset-password without auth
|
|
||||||
- Header shows real user initial and dropdown with logout per auth store
|
- Header shows real user initial and dropdown with logout per auth store
|
||||||
- Sidebar footer shows real user name and role per auth store
|
- Sidebar footer shows real user name and role per auth store
|
||||||
- All new UI strings use i18n t() function (no hardcoded text)
|
- All new UI strings use i18n t() function (no hardcoded text)
|
||||||
- Both de.json and en.json have all new auth/admin translation keys
|
- Both de.json and en.json have all new auth/header/admin translation keys
|
||||||
- Type-check passes for @tessera/web
|
- Type-check passes for @tessera/web
|
||||||
</acceptance_criteria>
|
</acceptance_criteria>
|
||||||
<done>Login page renders split-screen layout; unauthenticated users are redirected to /login; authenticated users see their name in header and sidebar; all strings are internationalized.</done>
|
<done>Login page renders split-screen layout per D-01; header shows user dropdown with logout; sidebar footer shows user info; all strings internationalized in DE/EN.</done>
|
||||||
</task>
|
</task>
|
||||||
|
|
||||||
<task type="auto">
|
<task type="auto">
|
||||||
<name>Task 2: User CRUD API + admin page and Tenant CRUD API + admin page</name>
|
<name>Task 3: User CRUD API + admin page and Tenant CRUD API + admin page</name>
|
||||||
<read_first>
|
<read_first>
|
||||||
- apps/api/src/user/user.service.ts (from Plan 02-01, user operations)
|
- apps/api/src/user/user.service.ts (from Plan 02-01, user operations)
|
||||||
- apps/api/src/user/user.module.ts (from Plan 02-01)
|
- apps/api/src/user/user.module.ts (from Plan 02-01)
|
||||||
@@ -349,7 +371,7 @@ See Plan 02-01 for the full artifacts table.
|
|||||||
</threat_model>
|
</threat_model>
|
||||||
|
|
||||||
<verification>
|
<verification>
|
||||||
After both tasks complete:
|
After all three tasks complete:
|
||||||
1. Navigate to http://localhost:3000 -- should redirect to /login
|
1. Navigate to http://localhost:3000 -- should redirect to /login
|
||||||
2. Login with admin/admin123 -- should redirect to dashboard, header shows "admin" user
|
2. Login with admin/admin123 -- should redirect to dashboard, header shows "admin" user
|
||||||
3. Navigate to /admin/users -- should show user table with the admin account
|
3. Navigate to /admin/users -- should show user table with the admin account
|
||||||
|
|||||||
Reference in New Issue
Block a user