Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/components/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const Input = forwardRef(
ref: Ref<HTMLInputElement>,
) => {
const wrapperClassNames = cn(
"flex w-fit gap-1.5 border rounded-[0.375rem] border-gray-30 focus-within:border-blue-20 placeholder:text-gray-40 ",
"flex w-fit gap-1.5 border rounded-[0.375rem] border-gray-30 focus-within:border-blue-20 placeholder:text-gray-40 bg-white",
{
"w-full": fullWidth,
"bg-gray-20 text-gray-40": disabled,
Expand Down Expand Up @@ -136,7 +136,7 @@ const TextArea = forwardRef(
ref: Ref<HTMLTextAreaElement>,
) => {
const wrapperClassNames = cn(
"flex w-fit gap-1.5 border rounded-[0.375rem] border-gray-30 focus-within:border-blue-20 placeholder:text-gray-40 ",
"flex w-fit gap-1.5 border rounded-[0.375rem] border-gray-30 focus-within:border-blue-20 placeholder:text-gray-40 bg-white",
{
"w-full": fullWidth,
"bg-gray-20 text-gray-40": disabled,
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/useUserStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface UserState {
user: User | null;
token: string | null;
isLoggedIn: boolean;
updateUser: (partial: Partial<User>) => void;
setUserAndToken: (user: User, token: string) => void;
updateShopId: (shopId: string) => void;
clearUser: () => void;
Expand All @@ -28,6 +29,14 @@ export const useUserStore = create<UserState>()(
token: null,
isLoggedIn: false,

updateUser: (partial) => {
const current = get();
if (!current.user) return;
set({
user: { ...current.user, ...partial },
});
},

setUserAndToken: (user, token) =>
set({
user,
Expand Down
181 changes: 101 additions & 80 deletions src/pages/ProfileEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const FIELD_LABELS: Record<keyof FormType, string> = {

export default function ProfileEditPage() {
const navigate = useNavigate();
const { user } = useUserStore();
const { user, updateUser } = useUserStore();
const [isSubmitting, setIsSubmitting] = useState(false);
const openModal = useModalStore((state) => state.openModal);

Expand All @@ -43,34 +43,52 @@ export default function ProfileEditPage() {
});

useEffect(() => {
async function fetchUser() {
if (!user) {
openModal({
type: "alert",
iconType: "warning",
message: "로그인 후에 이용 가능한 기능입니다.",
onClose: () => navigate(ROUTES.AUTH.SIGNIN),
});
return;
}
if (user.type === "employer") {
openModal({
type: "alert",
iconType: "warning",
message: "알바생 계정으로만 이용 가능한 기능입니다.",
onClose: () => navigate(ROUTES.SHOP.ROOT),
});
return;
}
}, []);

useEffect(() => {
const fetchUser = async () => {
if (!user?.id) return;
const res = await getUser(user.id);

if (!res.data.item?.name) {
navigate(ROUTES.PROFILE.REGISTER);
return;
}
const { name, phone, address, bio } = res.data.item;
setForm({
name: name ?? "",
phone: phone ?? "",
address,
bio: bio ?? "",
});
}
};
fetchUser();
}, [user?.id]);
}, [user?.id, navigate]);

const handleChange = (key: keyof FormType, value: string | SeoulDistrict) => {
setForm((prev) => ({ ...prev, [key]: value }));
};

const handleSubmit = async () => {
if (!user?.id) {
openModal({
type: "alert",
iconType: "warning",
message: "로그인 정보가 없습니다.",
});
return;
}

if (!user?.id) return;
if (isSubmitting) return;

const requiredFields: Array<keyof FormType> = ["name", "phone", "address"];
Expand Down Expand Up @@ -99,6 +117,7 @@ export default function ProfileEditPage() {

try {
await putUser(user.id, payload);
updateUser(payload);
openModal({
type: "message",
iconType: "none",
Expand All @@ -120,72 +139,74 @@ export default function ProfileEditPage() {
};

return (
<form
className="w-full max-w-[964px] mx-auto px-4 py-12"
onSubmit={(e) => {
e.preventDefault();
handleSubmit();
}}
>
<div className="flex justify-between items-center mb-8">
<h2 className="sm:text-[1.75rem] text-[1.25rem] font-bold">
내 프로필
</h2>
<button type="button" onClick={() => navigate("/profile")}>
<Close className="sm:w-8 sm:h-8 w-6 h-6 cursor-pointer" />
</button>
</div>

<div className="grid md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5 mb-6">
<TextField.Input
label="이름*"
placeholder="입력"
fullWidth
value={form.name}
onChange={(e) => handleChange("name", e.target.value)}
maxLength={20}
/>
<TextField.Input
label="연락처*"
placeholder="입력"
fullWidth
value={form.phone}
onChange={(e) => {
const formatted = autoHyphenFormatter(e.target.value);
handleChange("phone", formatted);
}}
/>
<Select
label="선호 지역*"
placeholder="선택"
fullWidth
options={SeoulDistricts.map((d) => ({ label: d, value: d }))}
value={form.address}
onValueChange={(value) => handleChange("address", value)}
/>
</div>
<div className="mb-10">
<TextField.TextArea
label="소개 (최대 300자)"
placeholder="입력"
fullWidth
rows={4}
value={form.bio}
maxLength={300}
onChange={(e) => handleChange("bio", e.target.value)}
/>
</div>
<div className="text-center">
<Button
variant="primary"
textSize="md"
className="sm:w-[350px] w-full px-34 py-3.5"
disabled={isSubmitting}
type="submit"
>
수정하기
</Button>
</div>
</form>
<div className="w-full bg-gray-5 min-h-screen">
<form
className="w-full max-w-[964px] mx-auto px-4 py-12"
onSubmit={(e) => {
e.preventDefault();
handleSubmit();
}}
>
<div className="flex justify-between items-center mb-8">
<h2 className="sm:text-[1.75rem] text-[1.25rem] font-bold">
내 프로필
</h2>
<button type="button" onClick={() => navigate("/profile")}>
<Close className="sm:w-8 sm:h-8 w-6 h-6 cursor-pointer" />
</button>
</div>

<div className="grid lg:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5 mb-6">
<TextField.Input
label="이름*"
placeholder="입력"
fullWidth
value={form.name}
onChange={(e) => handleChange("name", e.target.value)}
maxLength={20}
/>
<TextField.Input
label="연락처*"
placeholder="입력"
fullWidth
value={form.phone}
onChange={(e) => {
const formatted = autoHyphenFormatter(e.target.value);
handleChange("phone", formatted);
}}
/>
<Select
label="선호 지역*"
placeholder="선택"
fullWidth
options={SeoulDistricts.map((d) => ({ label: d, value: d }))}
value={form.address}
onValueChange={(value) => handleChange("address", value)}
/>
</div>
<div className="mb-10">
<TextField.TextArea
label="소개 (최대 300자)"
placeholder="입력"
fullWidth
rows={4}
value={form.bio}
maxLength={300}
onChange={(e) => handleChange("bio", e.target.value)}
/>
</div>
<div className="text-center">
<Button
variant="primary"
textSize="md"
className="sm:w-[350px] w-full px-34 py-3.5"
disabled={isSubmitting}
type="submit"
>
수정하기
</Button>
</div>
</form>
</div>
);
}
Loading