Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6c64283
Folder structure changes for shared files in Sign up and User Profile
Allimonae Jun 26, 2026
53dfca5
Folder structure changes for User Profile / Sign up form shared files
Allimonae Jun 26, 2026
7d82389
Added content to UserProfile.page
Allimonae Jun 26, 2026
681e5c6
Folder structure for members backend
Allimonae Jun 29, 2026
e224153
Member.java done
Allimonae Jun 29, 2026
a9d9de8
Updated field named in members db, MemberRepo.java done
Allimonae Jun 29, 2026
0093fba
Active updated to required, MemberDto.java done
Allimonae Jun 29, 2026
416f541
MemberService.java, modified return types in MemberRepo.java
Allimonae Jun 29, 2026
3854c2f
added dto records CreateMemberRequest and UpdateMemberRequest. Update…
Allimonae Jul 6, 2026
5adcade
Naming convention editMember -> updateMember in MemberRepo. Created M…
Allimonae Jul 6, 2026
9b98919
Added MemberController
Allimonae Jul 6, 2026
c4dcd8b
Removed member fields not accessible by frontend from Mem
Allimonae Jul 6, 2026
452abea
MemberController - Added id as param in updateMember, MemberService -…
Allimonae Jul 6, 2026
81c3966
Removed member files from common and moved to api/member
Allimonae Jul 10, 2026
bd7a82e
Updated imports due to folder structure change
Allimonae Jul 10, 2026
0bb6c11
Separated exceptions from MemberService.java, created new classes for…
Allimonae Jul 10, 2026
1eb7968
createMember implemented in MemberSqlRepo. createdAt and updatedAt ar…
Allimonae Jul 13, 2026
5e2af16
MemberSqlRepo createMember
Allimonae Jul 13, 2026
6276b0c
Restored V0001 change
Allimonae Jul 13, 2026
554d7b1
Updated application.yml to start db
Allimonae Jul 13, 2026
2052904
PR #55 minor changes
Allimonae Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions js/src/features/sign-up/components/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { signUpFormSchema } from "@/features/sign-up/api/schemas";
import {
INDUSTRIES,
MATCHING_PREFERENCES,
} from "@/features/sign-up/components/signUpFormConfig";
import { SignUpFormValues } from "@/features/sign-up/types";
import { userProfileSchema } from "@/features/user-profile/api/schemas";
import { UserProfileValues } from "@/features/user-profile/types";
import {
Alert,
Button,
Expand All @@ -27,7 +27,7 @@ const matchingPreferenceOptions = MATCHING_PREFERENCES.map((v) => ({
const industryOptions = INDUSTRIES.map((v) => ({ value: v, label: v }));

// Define initial values
const initialFormValues: SignUpFormValues = {
const initialFormValues: UserProfileValues = {
fullName: "",
email: "",
linkedin: "",
Expand All @@ -42,12 +42,12 @@ const initialFormValues: SignUpFormValues = {

export function SignUpForm() {
// State management
const [values, setValues] = useState<SignUpFormValues>(() => ({
const [values, setValues] = useState<UserProfileValues>(() => ({
...initialFormValues,
}));

const [errors, setErrors] = useState<
Partial<Record<keyof SignUpFormValues, string>>
Partial<Record<keyof UserProfileValues, string>>
>({});

const [isSubmitting, setIsSubmitting] = useState(false);
Expand All @@ -65,9 +65,9 @@ export function SignUpForm() {
};

// Handlers
const handleFieldChange = <K extends keyof SignUpFormValues>(
const handleFieldChange = <K extends keyof UserProfileValues>(
field: K,
value: SignUpFormValues[K],
value: UserProfileValues[K],
) => {
setValues((current) => ({ ...current, [field]: value }));
setErrors((current) => ({ ...current, [field]: undefined }));
Expand All @@ -76,11 +76,11 @@ export function SignUpForm() {
};

const handleFieldBlur = (
field: keyof SignUpFormValues,
field: keyof UserProfileValues,
overrideValue?: string,
) => {
const valueToValidate = overrideValue ?? values[field];
const fieldSchema = signUpFormSchema.pick({ [field]: true } as Record<
const fieldSchema = userProfileSchema.pick({ [field]: true } as Record<
typeof field,
true
>);
Expand All @@ -92,15 +92,15 @@ export function SignUpForm() {
};

// Validation logic
const validateForm = (values: SignUpFormValues) => {
const result = signUpFormSchema.safeParse(values);
const validateForm = (values: UserProfileValues) => {
const result = userProfileSchema.safeParse(values);
const fieldErrors =
result.success ? {} : result.error?.flatten().fieldErrors;
const errors = {} as Partial<Record<keyof SignUpFormValues, string>>;
const errors = {} as Partial<Record<keyof UserProfileValues, string>>;
for (const key in fieldErrors) {
if (fieldErrors[key as keyof SignUpFormValues]?.[0]) {
errors[key as keyof SignUpFormValues] =
fieldErrors[key as keyof SignUpFormValues]?.[0];
if (fieldErrors[key as keyof UserProfileValues]?.[0]) {
errors[key as keyof UserProfileValues] =
fieldErrors[key as keyof UserProfileValues]?.[0];
}
}
return errors;
Expand Down Expand Up @@ -211,7 +211,7 @@ export function SignUpForm() {
onChange={(value) =>
handleFieldChange(
"matchingPreference",
(value || "") as SignUpFormValues["matchingPreference"],
(value || "") as UserProfileValues["matchingPreference"],
)
}
onBlur={() => handleFieldBlur("matchingPreference")}
Expand All @@ -225,7 +225,7 @@ export function SignUpForm() {
onChange={(value) =>
handleFieldChange(
"industry",
(value || "") as SignUpFormValues["industry"],
(value || "") as UserProfileValues["industry"],
)
}
onBlur={() => handleFieldBlur("industry")}
Expand Down
8 changes: 0 additions & 8 deletions js/src/features/sign-up/components/signUpFormConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ export const MATCHING_PREFERENCES = [
"No Preference - I am open to any type of match",
] as const;

export const TALKING_POINTS = [
"Career journey",
"Advice",
"Current events",
"Hobbies",
"Other",
] as const;

export const INDUSTRIES = [
"Technology",
"Finance",
Expand Down
9 changes: 9 additions & 0 deletions js/src/features/user-profile/UserProfile.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack, Title } from "@mantine/core";

export function UserProfilePage() {
return (
<Stack gap="xl">
<Title order={2}>User Profile</Title>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

export const signUpFormSchema = z.object({
export const userProfileSchema = z.object({
fullName: z.string().min(1, "Full Name is required."),
email: z
.string()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface SignUpFormValues {
export interface UserProfileValues {
fullName: string;
email: string;
linkedin: string;
Expand Down
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@
<schemas>
<schema>public</schema>
</schemas>
<locations>
<location>filesystem:db/migration</location>
</locations>
<validateMigrationNaming>true</validateMigrationNaming>
</configuration>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.patinanetwork.patchats.api.member;

import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.patinanetwork.patchats.api.member.dto.CreateMemberRequest;
import org.patinanetwork.patchats.api.member.dto.MemberDto;
import org.patinanetwork.patchats.common.dto.ApiResponder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/members")
@Tag(name = "Member")
Comment thread
arklian marked this conversation as resolved.
@RequiredArgsConstructor
public class MemberController {

private final MemberService memberService;

@PostMapping
public ResponseEntity<ApiResponder<MemberDto>> createMember(@Valid @RequestBody final CreateMemberRequest request) {
final MemberDto response = memberService.createMember(request);
return ResponseEntity.ok(ApiResponder.success("Member created successfully", response));
}

// TODO: Implement these endpoints after createMember is fully functional and tested

Check warning on line 29 in src/main/java/org/patinanetwork/patchats/api/member/MemberController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-dYTMT2AZx_lxF&open=AZ9c2N-dYTMT2AZx_lxF&pullRequest=55
// @PatchMapping("/{id}")
// public ResponseEntity<ApiResponder<MemberDto>> updateMember(
// @Valid @RequestBody final UpdateMemberRequest request, @PathVariable final UUID id) {

Check warning on line 32 in src/main/java/org/patinanetwork/patchats/api/member/MemberController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-dYTMT2AZx_lxC&open=AZ9c2N-dYTMT2AZx_lxC&pullRequest=55
// final MemberDto response = memberService.updateMember(request, id);
// return ResponseEntity.ok(ApiResponder.success("Member updated successfully", response));
// }

// @GetMapping("/{id}")
// public ResponseEntity<ApiResponder<MemberDto>> getMember(@PathVariable final UUID id) {

Check warning on line 38 in src/main/java/org/patinanetwork/patchats/api/member/MemberController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-dYTMT2AZx_lxD&open=AZ9c2N-dYTMT2AZx_lxD&pullRequest=55
// final MemberDto response = memberService.getMemberById(id);
// return ResponseEntity.ok(ApiResponder.success("Member retrieved successfully", response));
// }

// @DeleteMapping("/{id}")
// public ResponseEntity<ApiResponder<MemberDto>> deactivateMember(@PathVariable final UUID id) {

Check warning on line 44 in src/main/java/org/patinanetwork/patchats/api/member/MemberController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-dYTMT2AZx_lxE&open=AZ9c2N-dYTMT2AZx_lxE&pullRequest=55
// final MemberDto response = memberService.deactivateMemberById(id);
// return ResponseEntity.ok(ApiResponder.success("Member deactivated successfully", response));
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.patinanetwork.patchats.api.member;

import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.patinanetwork.patchats.api.member.db.models.Member;
import org.patinanetwork.patchats.api.member.db.repos.MemberRepo;
import org.patinanetwork.patchats.api.member.dto.CreateMemberRequest;
import org.patinanetwork.patchats.api.member.dto.MemberDto;
import org.patinanetwork.patchats.api.member.dto.UpdateMemberRequest;
import org.patinanetwork.patchats.common.web.exception.MemberDuplicateException;
import org.patinanetwork.patchats.common.web.exception.MemberNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

@Service
@RequiredArgsConstructor
public class MemberService {

private final MemberRepo memberRepo;

public MemberDto createMember(CreateMemberRequest request) {
if (memberRepo.getMemberByEmail(request.email()).isPresent()) {
throw new MemberDuplicateException(request.email());
}
Member member = Member.builder()
.id(UUID.randomUUID())
.fullName(request.fullName())
.email(request.email())
.linkedInUrl(request.linkedInUrl())
.introduction(request.introduction())
.referralSource(request.referralSource())
.active(true)
.matchPref(request.matchPref())
.industryPref(request.industryPref())
.rolePref(request.rolePref())
.topics(request.topics())
.extraNotes(request.extraNotes())
.build();
Member createdMember = memberRepo.createMember(member);
return MemberDto.from(createdMember);
}

public MemberDto updateMember(UpdateMemberRequest request, UUID id) {
if (memberRepo.getMemberById(id).isEmpty()) {
throw new MemberNotFoundException(id);
}
// TODO: Implement partial update instead of full update

Check warning on line 48 in src/main/java/org/patinanetwork/patchats/api/member/MemberService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-HYTMT2AZx_lw-&open=AZ9c2N-HYTMT2AZx_lw-&pullRequest=55
Member member = Member.builder()
.id(id)
.fullName(request.fullName())
.email(request.email())
.linkedInUrl(request.linkedInUrl())
.introduction(request.introduction())
.matchPref(request.matchPref())
.industryPref(request.industryPref())
.rolePref(request.rolePref())
.topics(request.topics())
.extraNotes(request.extraNotes())
.build();
Member updatedMember = memberRepo
.updateMember(member)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Member not found"));
return MemberDto.from(updatedMember);
}

// TODO: Implement these methods after createMember and updateMember is fully functional and tested

Check warning on line 67 in src/main/java/org/patinanetwork/patchats/api/member/MemberService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-HYTMT2AZx_lxB&open=AZ9c2N-HYTMT2AZx_lxB&pullRequest=55
// public MemberDto getMemberById(UUID id) {

Check warning on line 68 in src/main/java/org/patinanetwork/patchats/api/member/MemberService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-HYTMT2AZx_lw_&open=AZ9c2N-HYTMT2AZx_lw_&pullRequest=55
// return memberRepo
// .getMemberById(id)
// .map(MemberDto::from)
// .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Member not found"));
// }

// public MemberDto deactivateMemberById(UUID id) {

Check warning on line 75 in src/main/java/org/patinanetwork/patchats/api/member/MemberService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=Patina-Network_patchats&issues=AZ9c2N-HYTMT2AZx_lxA&open=AZ9c2N-HYTMT2AZx_lxA&pullRequest=55
// return memberRepo
// .deactivateMemberById(id)
// .map(MemberDto::from)
// .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Member not found"));
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.patinanetwork.patchats.api.member.db.models;

import java.time.Instant;
import java.util.UUID;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Builder
@ToString
@EqualsAndHashCode
public class Member {

private UUID id;

@Setter
private String fullName;

@Setter
private String email;

@Setter
private String linkedInUrl;

@Setter
private String introduction;

@Setter
private String referralSource;

@Setter
private boolean active;

@Setter
private String matchPref;

@Setter
private String industryPref;

@Setter
private String rolePref;

@Setter
private String topics;

@Setter
private String extraNotes;

private Instant createdAt;

private Instant updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.patinanetwork.patchats.api.member.db.repos;

import java.util.Optional;
import java.util.UUID;
import org.patinanetwork.patchats.api.member.db.models.Member;

public interface MemberRepo {
/**
* @note - The provided object's methods will be overridden with any returned data from the database.
* @param member - required fields:
* <ul>
* <li>id
* <li>fullName
* <li>email
* <li>introduction
* <li>active
* <li>createdAt
* <li>updatedAt
* </ul>
*/
Member createMember(Member member);

/**
* @note - The provided object's methods will be overridden with any returned data from the database.
* @param member - overridden fields:
* <ul>
* <li>fullName
* <li>email
* <li>linkedInUrl
* <li>introduction
* <li>referralSource
* <li>active
* <li>matchPref
* <li>industryPref
* <li>rolePref
* <li>topics
* <li>extraNotes
* </ul>
*/
Optional<Member> updateMember(Member member);

Optional<Member> getMemberById(UUID id);

Optional<Member> getMemberByEmail(String email);

Optional<Member> deactivateMemberById(UUID id);
}
Loading
Loading