Repository: move-ev-sub/ccat
Pull request #119: Request first and last name upon signup and save those values to the database
Author: login: 1RoBe
Created: 2025-03-20T10:50:45Z | Merged: 2025-03-31T14:51:47Z

# Notes
- can't remove users from supabase authentication table via dashboard
- should I create a global/local interface for signUp which I export to both auth files?

# Changes
- added firstName and lastName variables to signUpSchema, services/auth.ts, actions/auth.ts
- added form for first and lastName variables
- tried different versions of passing objects as arguments

# Pictures
**.../auth/register**
![image](https://github.com/user-attachments/assets/87617f0f-0d61-4d29-80dc-593f84ed4d9b)

**Nothing entered**
![image](https://github.com/user-attachments/assets/e12441ae-a72e-458e-8ba5-1993f97814d7)

**Only firstName and lastName are NOT entered**
![image](https://github.com/user-attachments/assets/1da0a303-0ab2-4c84-b029-5159353aee9a)

**Database Before Registration**
![image](https://github.com/user-attachments/assets/7942b03b-ad5d-4a08-8e44-c5dabece88df)

**Database After Registration**
![image](https://github.com/user-attachments/assets/878f1963-474f-40b5-98f1-a0c4ad231036)
![image](https://github.com/user-attachments/assets/3566bce9-96cd-44aa-b801-705b25549a0e)





Diff:
diff --git a/src/components/forms/register.tsx b/src/components/forms/register.tsx
index f34ae80..75ddba9 100644
--- a/src/components/forms/register.tsx
+++ b/src/components/forms/register.tsx
@@ -67,6 +67,8 @@ export function RegisterForm() {
   const form = useForm<z.infer<typeof signUpSchema>>({
     resolver: zodResolver(signUpSchema),
     defaultValues: {
+      firstName: '',
+      lastName: '',
       email: '',
       password: '',
     },
@@ -88,6 +90,34 @@ export function RegisterForm() {
   return (
     <Form {...form}>
       <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
+        <div className="grid gap-8 lg:grid-cols-2">
+          <FormField
+            control={form.control}
+            name="firstName"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel>Vorname</FormLabel>
+                <FormControl>
+                  <Input type="text" placeholder="Max" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          <FormField
+            control={form.control}
+            name="lastName"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel>Nachname</FormLabel>
+                <FormControl>
+                  <Input type="text" placeholder="Mustermann" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+        </div>
         <FormField
           control={form.control}
           name="email"
diff --git a/src/server/actions/auth.ts b/src/server/actions/auth.ts
index d6cda26..6a67bf6 100644
--- a/src/server/actions/auth.ts
+++ b/src/server/actions/auth.ts
@@ -11,16 +11,10 @@ import { redirect } from 'next/navigation';
 import * as authService from '../services/auth';
 import { AuthActionResponse } from '../types/action-response';
 
-export async function signup({
-  email,
-  password,
-  acceptLegal,
-}: SignUpData): Promise<AuthActionResponse<null>> {
-  const parseRes = await signUpSchema.safeParseAsync({
-    email,
-    password,
-    acceptLegal,
-  });
+export async function signup(
+  signUpData: SignUpData
+): Promise<AuthActionResponse<null>> {
+  const parseRes = await signUpSchema.safeParseAsync(signUpData);
 
   if (!parseRes.success) {
     return {
@@ -29,7 +23,7 @@ export async function signup({
     };
   }
 
-  const res = await signUpWithEmail(email, password);
+  const res = await signUpWithEmail(signUpData);
 
   if (!res.ok) {
     return {
diff --git a/src/server/schemas/auth.ts b/src/server/schemas/auth.ts
index 2f270c1..57547dc 100644
--- a/src/server/schemas/auth.ts
+++ b/src/server/schemas/auth.ts
@@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
+    firstName: z.string().min(1, 'Der Vorname wird benötigt'),
+    lastName: z.string().min(1, 'Der Vorname wird benötigt'),
     email: z.string().email('Ungültige E-Mail-Adresse'),
     acceptLegal: z
       .boolean({
diff --git a/src/server/services/auth.ts b/src/server/services/auth.ts
index 4299dd8..bb9f8c6 100644
--- a/src/server/services/auth.ts
+++ b/src/server/services/auth.ts
@@ -6,28 +6,36 @@ import type { Session, SupabaseClient, User } from '@supabase/supabase-js';
 import { randomBytes } from 'crypto';
 import prisma from '../db';
 import { ServiceResult } from '../types/serviceResult';
+import { SignUpData } from '../schemas/auth';
 
 /**
  * Signs up a new user with email and password.
  *
+ * @param firstName - The first name of the user.
+ * @param lastName - The last name of the user.
  * @param email - The email of the user.
  * @param password - The password of the user.
  *
  * @returns A promise with the status of the sign up.
  */
+
 export async function signUpWithEmail(
-  email: string,
-  password: string
+  signUpData: SignUpData
 ): Promise<ServiceResult<{ user: User | null; session: Session | null }>> {
-  if (!email || !password) {
-    return { ok: false, error: 'Email and password are required for singup.' };
+  const { firstName, lastName, email, password } = signUpData;
+
+  if (!firstName || !lastName || !email || !password) {
+    return {
+      ok: false,
+      error: 'Your full Name, email and password are required for signup.',
+    };
   }
 
   const client = await createClient();
 
   const { error, data } = await client.auth.signUp({
-    email,
-    password,
+    email: email,
+    password: password,
   });
 
   if (error || !data?.user) {
@@ -49,8 +57,8 @@ export async function signUpWithEmail(
       email: email,
       userProfile: {
         create: {
-          firstName: '',
-          lastName: '',
+          firstName: firstName,
+          lastName: lastName,
           emailReminders: false,
           notifyMe: false,
         },


Reviews:
user: login: chris23lngr
body: Guter PR! An sich gefällt mir das ganze schon sehr gut, es gibt ein paar Kleinigkeiten, die man vielleicht überarbeiten müsste, schau einfach mal in die Kommentare.

Hier noch zwei Antworten auf deine Anmerkungen:

> * can't remove users from supabase authentication table via dashboard

Das ist ein bekanntest Issue mit der lokalen Supabase Entwicklungsumgebung ([#32901](https://github.com/supabase/supabase/issues/32901)). Solange der fix noch nicht in die prod gepusht wurde, muss man die Datenbank immer einmal komplette zurücksetzen (`pnpm db:stop` -> Alle *volumes* löschen -> `pnpm db:start` -> `pnpm prisma migrate dev`)

> * should I create a global/local interface for signUp which I export to both auth files?

An sich ist das eine gute Idee! Das interface dann bitte in `/server/types/auth.ts` implementieren und in den entsprechenden Files importieren.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-21T12:07:24Z
user: login: 1RoBe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T19:36:23Z
user: login: chris23lngr
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T19:39:06Z
user: login: chris23lngr
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T19:39:24Z
user: login: 1RoBe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T19:40:32Z
user: login: 1RoBe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T19:58:52Z
user: login: 1RoBe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T19:59:24Z
user: login: 1RoBe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T19:59:54Z
user: login: chris23lngr
body: Die Änderungen sehen gut aus! Danke dir, ich merge eben.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-31T14:51:31Z

Review comments:
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
path: src/server/schemas/auth.ts
user: login: chris23lngr
body: Ja, ist eigentlich eine gute Idee. Dafür müsstest du dir wahrscheinlich zod mal genauer anschauen: [https://zod.dev/?id=refine](https://zod.dev/?id=refine)
created_at: 2025-03-21T11:48:35Z
author_association: MEMBER
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
+    firstName: z.string({ message: 'Vorname darf nur Buchstaben enthalten' }),
path: src/server/schemas/auth.ts
user: login: chris23lngr
body: Die `message` passt nicht ganz: Diese message wird angezeigt, wenn das Feld im Fomular *nicht* ausgefüllt wurde. Ich würde hier eher eine Nachricht wie "Dieses Feld wird benötigt" eintragen.

Du könntest hier auch noch einen modifier anhängen, der bestimmt, dass das Feld mindestens 1 Zeichen enthalten muss:

```ts
firstName: z.string().min(1, "Der Vorname wird benötigt"),
```
Oder so ähnlich, wie du es für richtig hältst. Hier kannst du dir mal `refinements` von `zod` anschauen: [https://zod.dev/?id=refine](https://zod.dev/?id=refine)

created_at: 2025-03-21T11:49:56Z
author_association: MEMBER
diff_hunk: @@ -10,24 +10,48 @@ import { ServiceResult } from '../types/serviceResult';
 /**
  * Signs up a new user with email and password.
  *
+ * @param firstName - The first name of the user.
+ * @param lastName - The last name of the user.
  * @param email - The email of the user.
  * @param password - The password of the user.
  *
  * @returns A promise with the status of the sign up.
  */
+
+interface UserRegistration {
+  firstName: string;
+  lastName: string;
+  email: string;
+  password: string;
+}
+
 export async function signUpWithEmail(
-  email: string,
-  password: string
+  userReg: UserRegistration
 ): Promise<ServiceResult<{ user: User | null; session: Session | null }>> {
-  if (!email || !password) {
-    return { ok: false, error: 'Email and password are required for singup.' };
+  if (
+    !userReg.firstName ||
+    !userReg.lastName ||
+    !userReg.email ||
+    !userReg.password
+  ) {
+    return {
+      ok: false,
+      error: 'Your full Name, email and password are required for singup.',
+    };
   }
 
   const client = await createClient();
 
   const { error, data } = await client.auth.signUp({
-    email,
-    password,
+    email: userReg.email,
+    password: userReg.password,
+    // Is adding the data object necessary, since we create the user profile with prisma?
path: src/server/services/auth.ts
user: login: chris23lngr
body: Gute Frage! Nein, wir müssen das Datenobjekt nicht an Supabase weitergeben, weil wir (genau wie du schon sagst) unsere Nutzerdaten separat verwalten. Zeilen 48 - 54 können entfernt werden.
created_at: 2025-03-21T11:52:01Z
author_association: MEMBER
diff_hunk: @@ -10,24 +10,48 @@ import { ServiceResult } from '../types/serviceResult';
 /**
  * Signs up a new user with email and password.
  *
+ * @param firstName - The first name of the user.
+ * @param lastName - The last name of the user.
  * @param email - The email of the user.
  * @param password - The password of the user.
  *
  * @returns A promise with the status of the sign up.
  */
+
+interface UserRegistration {
+  firstName: string;
+  lastName: string;
+  email: string;
+  password: string;
+}
+
 export async function signUpWithEmail(
-  email: string,
-  password: string
+  userReg: UserRegistration
 ): Promise<ServiceResult<{ user: User | null; session: Session | null }>> {
-  if (!email || !password) {
-    return { ok: false, error: 'Email and password are required for singup.' };
+  if (
+    !userReg.firstName ||
path: src/server/services/auth.ts
user: login: chris23lngr
body: Hier würde ich das `userReg`-Objekt destrukturieren. Dann musst du nicht immer `userReg.firstName` schreiben:

```ts
const {
  firstName,
  lastName,
  email,
  password
} = userReg;
```
created_at: 2025-03-21T11:56:50Z
author_association: MEMBER
path: src/components/forms/register.tsx
user: login: chris23lngr
body: Das sieht gut aus! In den Feldern Vor- und Nachname werden ja nicht so lange Texte eingetragen, da könnte man überlegen, ob man die Felder auf größeren Bildschirmen in einer Zeile/nebeneinander darstellt. Schau dir dazu mal in der Datei `create-sub-event.tsx` die Felder `name` und `company` an. Die sind in einem div gewrapped mit einem Grid-Layout. Du kannst ja mal ausprobieren, ob das mit dem Vor- und Nachnamen auch funktioniert - wenns passt, dann gerne implementieren, ansonsten einfach weglassen.
created_at: 2025-03-21T12:03:26Z
author_association: MEMBER
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
path: src/server/schemas/auth.ts
user: login: 1RoBe
body: Hab nochmal drüber nachgedacht, aber wäre natürlich für ausländische Studierende mit Namen bestehend aus nicht-lateinischen Buchstaben blöd
created_at: 2025-03-28T19:36:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
path: src/server/schemas/auth.ts
user: login: chris23lngr
body: https://www.sqlservercentral.com/articles/proper-storage-and-handling-of-personal-names
created_at: 2025-03-28T19:39:05Z
author_association: MEMBER
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
path: src/server/schemas/auth.ts
user: login: chris23lngr
body: Bin morgen wieder verfügbar und schaue es mir dann auch nochmal genauer an
created_at: 2025-03-28T19:39:23Z
author_association: MEMBER
diff_hunk: @@ -10,24 +10,48 @@ import { ServiceResult } from '../types/serviceResult';
 /**
  * Signs up a new user with email and password.
  *
+ * @param firstName - The first name of the user.
+ * @param lastName - The last name of the user.
  * @param email - The email of the user.
  * @param password - The password of the user.
  *
  * @returns A promise with the status of the sign up.
  */
+
+interface UserRegistration {
+  firstName: string;
+  lastName: string;
+  email: string;
+  password: string;
+}
+
 export async function signUpWithEmail(
-  email: string,
-  password: string
+  userReg: UserRegistration
 ): Promise<ServiceResult<{ user: User | null; session: Session | null }>> {
-  if (!email || !password) {
-    return { ok: false, error: 'Email and password are required for singup.' };
+  if (
+    !userReg.firstName ||
path: src/server/services/auth.ts
user: login: 1RoBe
body: Hab jetzt einfach den SignUpData type genutzt. Der Nachteil ist hierbei, das der `acceptLegal` Wert der Funktion `signUpWithEmail` übergeben wird obwohl diese den nicht benötigt. vllt. fällt dir was eleganteres ein :)
created_at: 2025-03-28T19:40:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,24 +10,48 @@ import { ServiceResult } from '../types/serviceResult';
 /**
  * Signs up a new user with email and password.
  *
+ * @param firstName - The first name of the user.
+ * @param lastName - The last name of the user.
  * @param email - The email of the user.
  * @param password - The password of the user.
  *
  * @returns A promise with the status of the sign up.
  */
+
+interface UserRegistration {
+  firstName: string;
+  lastName: string;
+  email: string;
+  password: string;
+}
+
 export async function signUpWithEmail(
-  email: string,
-  password: string
+  userReg: UserRegistration
 ): Promise<ServiceResult<{ user: User | null; session: Session | null }>> {
-  if (!email || !password) {
-    return { ok: false, error: 'Email and password are required for singup.' };
+  if (
+    !userReg.firstName ||
+    !userReg.lastName ||
+    !userReg.email ||
+    !userReg.password
+  ) {
+    return {
+      ok: false,
+      error: 'Your full Name, email and password are required for singup.',
+    };
   }
 
   const client = await createClient();
 
   const { error, data } = await client.auth.signUp({
-    email,
-    password,
+    email: userReg.email,
+    password: userReg.password,
+    // Is adding the data object necessary, since we create the user profile with prisma?
path: src/server/services/auth.ts
user: login: 1RoBe
body: hab ich entfernt
created_at: 2025-03-28T19:58:52Z
author_association: CONTRIBUTOR
path: src/components/forms/register.tsx
user: login: 1RoBe
body: ![image](https://github.com/user-attachments/assets/d343f37e-5ced-43df-974d-2c8ea458f82d)

created_at: 2025-03-28T19:59:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,6 +50,9 @@ const generatePasswordSchema = (): z.ZodObject<{ password: z.ZodString }> => {
 
 export const signUpSchema = z
   .object({
+    // Should names be limited to ASCII characters?
+    firstName: z.string({ message: 'Vorname darf nur Buchstaben enthalten' }),
path: src/server/schemas/auth.ts
user: login: 1RoBe
body: hab ich mal so übernommen
created_at: 2025-03-28T19:59:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-20T10:50:52Z
body: [vc]: #13JZnktz3lGCutOGgWBzQ/RswlBzkBFAsu1GlACn64Q=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJjY2F0IiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImNjYXQtZ2l0LWZlYXQtcmVxdWVzdC1maXJzdC1hbmQtbGFzdC1uYW1lLXVwb24tc2lnbnVwLW1vdmUtZXYudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbW92ZS1ldi9jY2F0L0FzVjllU0tBVDZ4YjR0ZUE3blJBbmdxQ1hHQ1ciLCJwcmV2aWV3VXJsIjoiY2NhdC1naXQtZmVhdC1yZXF1ZXN0LWZpcnN0LWFuZC1sYXN0LW5hbWUtdXBvbi1zaWdudXAtbW92ZS1ldi52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwicm9vdERpcmVjdG9yeSI6bnVsbH1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **ccat** | ✅ Ready ([Inspect](https://vercel.com/move-ev/ccat/AsV9eSKAT6xb4teA7nRAngqCXGCW)) | [Visit Preview](https://ccat-git-feat-request-first-and-last-name-upon-signup-move-ev.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/ccat-git-feat-request-first-and-last-name-upon-signup-move-ev.vercel.app?via=pr-comment-feedback-link) | Mar 28, 2025 8:00pm |


author_association: NONE
user: login: 1RoBe
created_at: 2025-03-20T10:51:44Z
body: @chris23lngr gerne eine Rückmeldung wie es am klügsten wäre, objekte als argumente zu übergeben :)
Hab auch Teile des codes commented, bei denen ich mir nicht sicher bin ob das sinnvoll ist, was ich da getan hab 
author_association: CONTRIBUTOR
user: login: chris23lngr
created_at: 2025-03-21T12:09:14Z
body: > gerne eine Rückmeldung wie es am klügsten wäre, objekte als argumente zu übergeben :)

Hier verstehe ich nicht ganz genau wie du das meinst? So wie du die Parameterübergabe an die Methode machst (als Objekt), hätte ich es auch gemacht. Oder meinst du was anderes?
author_association: MEMBER
user: login: 1RoBe
created_at: 2025-03-28T20:05:26Z
body: Fix:
- remove unnecessary supabase object transfer
- use SignUpData type as argument in `signUpWithEmail` and `signUp` functions
- make the fields 'vorname' and 'nachname' inline with div with gridlayout
- changed requirements for 'vorname' and nachname' input
author_association: CONTRIBUTOR