Skip to content

Commit 7da0395

Browse files
authored
Merge pull request NCUAppTeam#158 from boyiliu1007/signup
feat: signup procedure
2 parents c6d36eb + 044f1df commit 7da0395

File tree

7 files changed

+539
-385
lines changed

7 files changed

+539
-385
lines changed

package-lock.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/backend/user/Controllers/UserController.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { supabase } from "../../../utils/supabase";
33

44
import User, { DBUser } from '../Entities/User';
55
import UserService from '../Services/UserService';
6-
6+
import UserSignupData from "../Entities/UserSignupData";
77

88
const USER_TABLE_NAME = "members"
99

@@ -111,6 +111,87 @@ export default class UserController {
111111

112112
return user
113113
}
114+
115+
/**
116+
* Create a new user
117+
* (it will create a new row in supabase users table and a new row in members table
118+
* the supabse user table is used for authentication, and the members table is used for user information
119+
* )
120+
*
121+
* @usage userController.createUser(<PARAMS>).then(
122+
* (user: User) => { ... }
123+
* )
124+
*
125+
* @param {UserSignupData} userSignupData - The user data to create
126+
*
127+
* @returns {User} - The created user entity
128+
*
129+
* @author Boyiliu (@boyiliu1007)
130+
*/
114131

132+
public async createUser(userSignupData: UserSignupData): Promise<User | null> {
133+
134+
// should signup in users table at auth schema
135+
const { data, error } = await supabase.auth.signUp({
136+
email: userSignupData.email,
137+
password: userSignupData.password,
138+
})
139+
if (error) {
140+
// handle auth error cannot use handleSupabaseError
141+
console.log(error.name);
142+
return null
143+
}
144+
if (!data)
145+
return null
115146

147+
// get user id from users table at auth schema
148+
const userID = data.user?.id
149+
150+
// insert into members table at public schema
151+
const { error: insertMemberError } = await supabase
152+
.from(USER_TABLE_NAME)
153+
.insert(
154+
{
155+
uuid: userID,
156+
name: userSignupData.name,
157+
email: userSignupData.email,
158+
username: userSignupData.username,
159+
studentId: userSignupData.studentId,
160+
identity: 2,
161+
created_at: new Date().toISOString(),
162+
}
163+
)
164+
.single();
165+
if (insertMemberError) {
166+
console.log("Error inserting member:", insertMemberError);
167+
ErrorHandler.handleSupabaseError(insertMemberError);
168+
return null;
169+
}
170+
171+
// get the inserted member data
172+
// fetch the newly created user
173+
const { data: memberData, error: getMemberError } = await supabase
174+
.from(USER_TABLE_NAME)
175+
.select("*")
176+
.eq("uuid", userID)
177+
.single();
178+
179+
if (getMemberError) {
180+
console.error("Error retrieving member data:", getMemberError);
181+
ErrorHandler.handleSupabaseError(getMemberError);
182+
return null;
183+
}
184+
185+
console.log("User created successfully:", memberData);
186+
187+
// NOTE: Signing out immediately after signup may be unexpected—
188+
// please confirm if automatic sign-in is not desired.
189+
const { error: signoutError } = await supabase.auth.signOut();
190+
if (signoutError) {
191+
console.error("Sign-out error:", signoutError);
192+
// proceed even if sign-out fails
193+
}
194+
195+
return UserService.parseUser(memberData);
196+
}
116197
}

src/backend/user/Entities/User.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type DBUser = Database['public']['Tables']['members']['Row'];
1414
export default class User {
1515
public id: string = ""
1616
public username: string = ""
17+
public name: string = ""
1718
public email: string = ""
1819
public phone: string = ""
1920
public avatar: string = ""
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
export default class UserSignupData {
3+
public name: string = ''
4+
public email: string = ''
5+
public password: string = ''
6+
public studentId: string = ''
7+
public username: string = ''
8+
9+
constructor(userSignupData: { name: string, email: string, password: string, studentId: string, username: string }) {
10+
this.name = userSignupData.name;
11+
this.email = userSignupData.email;
12+
this.password = userSignupData.password;
13+
this.studentId = userSignupData.studentId;
14+
this.username = userSignupData.username;
15+
}
16+
}

src/backend/user/Services/UserService.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import User, { DBUser } from "../Entities/User";
22

3+
34
const UserService = {
45

56
/**
@@ -20,14 +21,39 @@ const UserService = {
2021
const user = new User()
2122

2223
user.id = record.uuid
23-
user.username = record.name
24-
user.email = record.fk_email
25-
user.identity = record.fk_identity
24+
if(record.username)
25+
user.username = record.username
26+
27+
user.name = record.name
28+
if(record.phone){
29+
user.phone = record.phone
30+
}else{
31+
user.phone = ""
32+
}
33+
user.joinedAt = new Date(record.created_at)
34+
if(record.profileBackground) {
35+
user.profileBackground = record.profileBackground
36+
} else {
37+
user.profileBackground = ""
38+
}
39+
if(record.department) {
40+
user.department = record.department
41+
}else{
42+
user.department = ""
43+
}
44+
if(record.grade){
45+
user.grade = record.grade.toString()
46+
}else{
47+
user.grade = ""
48+
}
49+
if(record.bio) user.bio = record.bio
50+
else user.bio = ""
51+
user.email = record.email
52+
user.identity = record.identity
2653
user.avatar = record.avatar
2754

2855
return user
2956
}
30-
3157
}
3258

3359
export default UserService

src/routes/signup.tsx

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { createFileRoute, useNavigate, useRouterState } from '@tanstack/react-router';
22
import { useEffect, useRef, useState } from 'react';
33
import zxcvbn from 'zxcvbn'; //記得要先安裝zxcvbn,輸入 npm install zxcvbn
4+
import UserController from '../backend/user/Controllers/UserController';
5+
import UserSignupData from '../backend/user/Entities/UserSignupData';
46
export const Route = createFileRoute('/signup')({
5-
component: SignUpPage,
7+
component: SignUpPage,
68
})
79

810
function SignUpPage() {
@@ -22,7 +24,7 @@ function SignUpPage() {
2224
return (
2325
<div className="max-w-xl mx-auto mt-10 p-6 bg-white shadow-md rounded-lg">
2426
{userData ?
25-
(<SignUpForm userInfo={userData} />) :
27+
(<SignUpForm userInfo={userData} navigate={navigate} />) :
2628
// TODO:之後可以引入404頁面
2729
<div className="text-center">
2830
<h1 className="text-2xl font-bold text-gray-900">Something Went Wrong! Please Contact ncuappteam@gmail.com</h1>
@@ -40,7 +42,7 @@ interface UserInfo {
4042
studentId: string
4143
}
4244

43-
function SignUpForm({ userInfo }: { userInfo: UserInfo }) {
45+
function SignUpForm({ userInfo, navigate }: { userInfo: UserInfo; navigate: ReturnType<typeof useNavigate> }) {
4446
const [formData, setFormData] = useState({
4547
nickname: '',
4648
password: '',
@@ -104,19 +106,49 @@ function SignUpForm({ userInfo }: { userInfo: UserInfo }) {
104106
nickname: trimmedNickname,
105107
password: formData.password,
106108
})
107-
// 加上 API 請求來提交「nickname」和「password」
109+
110+
setIsLoading(true)
108111
try {
109-
await new Promise((resolve) => setTimeout(resolve, 2000)) // 🔥 假裝等待伺服器回應
110-
console.log('Submitted Data:', {
111-
nickname: trimmedNickname,
112+
const signupData: UserSignupData = {
113+
name: userInfo.chineseName,
114+
email: userInfo.email,
112115
password: formData.password,
113-
})
114-
alert('註冊成功!')
116+
username: trimmedNickname,
117+
studentId: userInfo.studentId,
118+
}
119+
const user = await addUser(signupData)
120+
if (!user) {
121+
alert('註冊失敗,請檢查您的資料並重試。')
122+
return
123+
}
124+
console.log('User created successfully:')
125+
console.log(window.location.href)
126+
127+
navigate({ to: "/login", search:{redirect: "/"}})
115128
} catch (error) {
129+
console.log(error)
116130
console.error('註冊失敗:', error)
117131
alert('伺服器錯誤,請稍後再試')
118132
} finally {
119-
setIsLoading(false) // 🔥 關閉 Loading
133+
setIsLoading(false)
134+
}
135+
}
136+
137+
// send formData + userInfo to supabase
138+
async function addUser(userSignupData: UserSignupData) {
139+
140+
const signupInstance = new UserSignupData(userSignupData)
141+
const userController = new UserController()
142+
const user = await userController.createUser(signupInstance);
143+
if (!user) {
144+
// Handle error case
145+
console.error("Failed to create user");
146+
alert('註冊失敗,請檢查您的資料並重試。')
147+
return null
148+
} else {
149+
// Success case - use the user object
150+
console.log("User created:", user);
151+
return user
120152
}
121153
}
122154

0 commit comments

Comments
 (0)