From 7b50a3dce28004ab883b7d5c707154aafd10f104 Mon Sep 17 00:00:00 2001 From: Tyrrnien81 Date: Sat, 21 Jun 2025 21:22:52 -0400 Subject: [PATCH] feat: implement Slice A - Enhanced User Registration & Profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add firstName and lastName fields to User model with database migration - Update signup/login APIs to handle name fields with validation (1-50 chars) - Enhance SignupForm with first name and last name input fields - Update auth store and API service interfaces for name fields - Display real user names in Dashboard welcome message and Account section - Update E2E tests to include name field validation and display verification Resolves user journey: 'As a user, I want to provide my full name during signup and see it reflected throughout the app' Changes: - Database: Added first_name, last_name columns to users table - Backend: Extended signup/login validation and response schemas - Frontend: Enhanced registration form and user data display - Testing: Updated E2E tests for complete signup → dashboard flow --- .../migration.sql | 17 + backend/prisma/schema.prisma | 2 + backend/src/routes/auth.js | 8 +- backend/src/utils/validation.js | 36 +- frontend/src/components/auth/SignupForm.tsx | 49 +- frontend/src/pages/Dashboard.tsx | 979 ++++++++++-------- frontend/src/services/api.ts | 6 +- frontend/src/stores/authStore.ts | 13 +- tests/e2e/auth.spec.js | 15 + 9 files changed, 691 insertions(+), 434 deletions(-) create mode 100644 backend/prisma/migrations/20250622010625_add_user_names/migration.sql diff --git a/backend/prisma/migrations/20250622010625_add_user_names/migration.sql b/backend/prisma/migrations/20250622010625_add_user_names/migration.sql new file mode 100644 index 0000000..3f75c5f --- /dev/null +++ b/backend/prisma/migrations/20250622010625_add_user_names/migration.sql @@ -0,0 +1,17 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_users" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "first_name" TEXT NOT NULL DEFAULT '', + "last_name" TEXT NOT NULL DEFAULT '', + "pass_hash" TEXT NOT NULL, + "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO "new_users" ("created_at", "email", "id", "pass_hash") SELECT "created_at", "email", "id", "pass_hash" FROM "users"; +DROP TABLE "users"; +ALTER TABLE "new_users" RENAME TO "users"; +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 7c9b9e0..690c85f 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -13,6 +13,8 @@ datasource db { model User { id Int @id @default(autoincrement()) email String @unique + firstName String @default("") @map("first_name") + lastName String @default("") @map("last_name") passHash String @map("pass_hash") createdAt DateTime @default(now()) @map("created_at") diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index 65a652d..bea98da 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -22,7 +22,7 @@ const router = Router(); */ router.post("/signup", validateRequest(signupSchema), async (req, res) => { try { - const { email, password } = req.validatedData; + const { email, firstName, lastName, password } = req.validatedData; // Check if user already exists const existingUser = await prisma.user.findUnique({ @@ -43,11 +43,15 @@ router.post("/signup", validateRequest(signupSchema), async (req, res) => { const user = await prisma.user.create({ data: { email, + firstName, + lastName, passHash, }, select: { id: true, email: true, + firstName: true, + lastName: true, createdAt: true, }, }); @@ -120,6 +124,8 @@ router.post("/login", validateRequest(loginSchema), async (req, res) => { user: { id: user.id, email: user.email, + firstName: user.firstName, + lastName: user.lastName, createdAt: user.createdAt, }, tokens: { diff --git a/backend/src/utils/validation.js b/backend/src/utils/validation.js index b729815..8b9ead9 100644 --- a/backend/src/utils/validation.js +++ b/backend/src/utils/validation.js @@ -3,6 +3,14 @@ import { z } from "zod"; // Auth validation schemas export const signupSchema = z.object({ email: z.string().email("Invalid email format"), + firstName: z + .string() + .min(1, "First name is required") + .max(50, "First name must be less than 50 characters"), + lastName: z + .string() + .min(1, "Last name is required") + .max(50, "Last name must be less than 50 characters"), password: z .string() .min(8, "Password must be at least 8 characters") @@ -29,22 +37,26 @@ export const eb1aAssessmentSchema = z.object({ z.string(), z.array(z.string()), z.object({ - user: z.object({ - educationLevel: z.string().optional() - }).optional(), - startupAchievements: z.object({ - funding: z.string().optional(), - traction: z.string().optional(), - awards: z.array(z.string()).optional(), - patents: z.array(z.string()).optional() - }).optional(), + user: z + .object({ + educationLevel: z.string().optional(), + }) + .optional(), + startupAchievements: z + .object({ + funding: z.string().optional(), + traction: z.string().optional(), + awards: z.array(z.string()).optional(), + patents: z.array(z.string()).optional(), + }) + .optional(), media: z.array(z.string()).optional(), speakingExperience: z.array(z.string()).optional(), publications: z.array(z.string()).optional(), references: z.array(z.string()).optional(), - usContacts: z.array(z.string()).optional() - }) - ]) + usContacts: z.array(z.string()).optional(), + }), + ]), }); /** diff --git a/frontend/src/components/auth/SignupForm.tsx b/frontend/src/components/auth/SignupForm.tsx index fb3d0ff..d52eb16 100644 --- a/frontend/src/components/auth/SignupForm.tsx +++ b/frontend/src/components/auth/SignupForm.tsx @@ -7,6 +7,8 @@ interface SignupFormProps { const SignupForm: React.FC = ({ onSwitchToLogin }) => { const [formData, setFormData] = useState({ + firstName: "", + lastName: "", email: "", password: "", confirmPassword: "", @@ -34,7 +36,12 @@ const SignupForm: React.FC = ({ onSwitchToLogin }) => { return; } - const result = await signup(formData.email, formData.password); + const result = await signup( + formData.email, + formData.firstName, + formData.lastName, + formData.password + ); if (result.success) { console.log("Signup successful:", result.user); @@ -43,6 +50,8 @@ const SignupForm: React.FC = ({ onSwitchToLogin }) => { const isFormValid = () => { return ( + formData.firstName.trim() && + formData.lastName.trim() && formData.email && formData.password && formData.confirmPassword && @@ -67,6 +76,44 @@ const SignupForm: React.FC = ({ onSwitchToLogin }) => { )}
+
+ + +
+ +
+ + +
+