From 9711b50b5f7204957bb06364601911cb45ead08f Mon Sep 17 00:00:00 2001 From: Jay Sheen Date: Mon, 29 Sep 2025 09:24:23 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EA=B4=80=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿค– Generated with Claude Code Co-Authored-By: Claude --- Makefile | 2 +- .../account/ui/password-manage-wrap.tsx | 6 +- .../account/ui/user-login-auth-info-wrap.tsx | 20 ++- ...se-user-change-cancel-password-mutation.ts | 29 +++++ src/entities/user/model/types.ts | 12 +- src/pages/account/account-pages.tsx | 2 + .../password/modify-cancel-password-page.tsx | 121 ++++++++++++++++++ .../password/modify-login-password-page.tsx | 23 +--- src/pages/account/user/add-account-page.tsx | 12 +- src/pages/account/user/menu-auth-page.tsx | 12 +- src/shared/api/api-url-user.ts | 3 + src/shared/constants/paths.ts | 4 + src/shared/constants/route-names.ts | 5 +- 13 files changed, 219 insertions(+), 32 deletions(-) create mode 100644 src/entities/user/api/use-user-change-cancel-password-mutation.ts create mode 100644 src/pages/account/password/modify-cancel-password-page.tsx diff --git a/Makefile b/Makefile index 379ab95..f833e2a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ NODE_VERSION = 20 DOCKER_REGISTRY = harbor.jongyeob.com DOCKER_PROJECT = nicepay DOCKER_IMAGE = nice-app-web -DOCKER_TAG = latest +DOCKER_TAG = 1.0.0 DOCKER_FULL_IMAGE = $(DOCKER_REGISTRY)/$(DOCKER_PROJECT)/$(DOCKER_IMAGE) CONTAINER_NAME = nice-app-web PORT = 80 diff --git a/src/entities/account/ui/password-manage-wrap.tsx b/src/entities/account/ui/password-manage-wrap.tsx index e0fa6e8..c53c013 100644 --- a/src/entities/account/ui/password-manage-wrap.tsx +++ b/src/entities/account/ui/password-manage-wrap.tsx @@ -4,9 +4,6 @@ import { useNavigate } from '@/shared/lib/hooks/use-navigate'; export const PasswordManageWrap = () => { const { navigate } = useNavigate(); - const onClickTonavigation = () => { - navigate(PATHS.account.password.modifyLoginPassword); - }; return ( <>
@@ -15,11 +12,12 @@ export const PasswordManageWrap = () => {
diff --git a/src/entities/account/ui/user-login-auth-info-wrap.tsx b/src/entities/account/ui/user-login-auth-info-wrap.tsx index d0d7823..bd75f38 100644 --- a/src/entities/account/ui/user-login-auth-info-wrap.tsx +++ b/src/entities/account/ui/user-login-auth-info-wrap.tsx @@ -3,6 +3,9 @@ import { useUserFindAuthMethodMutation } from '@/entities/user/api/use-user-find import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant'; import { useEffect, useState } from 'react'; import { useUserModifyAuthMethodMutation } from '@/entities/user/api/use-user-modify-authmethod-mutation'; +import { snackBar } from '@/shared/lib/toast'; +import { PATHS } from '@/shared/constants/paths'; +import { useNavigate } from '@/shared/lib/hooks/use-navigate'; export const UserLoginAuthInfoWrap = ({ mid, @@ -10,8 +13,7 @@ export const UserLoginAuthInfoWrap = ({ idCl, status, }: UserFindAuthMethodParams) => { - const { mutateAsync: userFindAuthMethod } = useUserFindAuthMethodMutation(); - const { mutateAsync: userModifyAuthMethod } = useUserModifyAuthMethodMutation(); + const { navigate } = useNavigate(); const [pageParam] = useState(DEFAULT_PAGE_PARAM); const [authMethodData, setAuthMethodData] = useState(); const [initialData, setInitialData] = useState(); @@ -21,6 +23,20 @@ export const UserLoginAuthInfoWrap = ({ const [editablePhoneIndex, setEditablePhoneIndex] = useState(-1); const [readOnlyEmails, setReadOnlyEmails] = useState>(new Set()); const [readOnlyPhones, setReadOnlyPhones] = useState>(new Set()); + const { mutateAsync: userFindAuthMethod } = useUserFindAuthMethodMutation(); + const { mutateAsync: userModifyAuthMethod } = useUserModifyAuthMethodMutation({ + onSuccess: () => { + snackBar('์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + navigate(PATHS.account.user.manage, { + state: { + mid: mid, + } + }); + }, + onError: (error) => { + snackBar(error?.response?.data?.message || '์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.'); + } + }); console.log("UserLoginAuthInfoWrap", mid, usrid, idCl, status); diff --git a/src/entities/user/api/use-user-change-cancel-password-mutation.ts b/src/entities/user/api/use-user-change-cancel-password-mutation.ts new file mode 100644 index 0000000..9c1f9c5 --- /dev/null +++ b/src/entities/user/api/use-user-change-cancel-password-mutation.ts @@ -0,0 +1,29 @@ +import axios from 'axios'; +import { API_URL_USER } from '@/shared/api/api-url-user'; +import { resultify } from '@/shared/lib/resultify'; +import { CBDCAxiosError } from '@/shared/@types/error'; +import { + ChangeCancelPasswordParams, + ChangeCancelPasswordResponse +} from '../model/types'; +import { + useMutation, + UseMutationOptions +} from '@tanstack/react-query'; + +export const userChangeCancelPassword = (params: ChangeCancelPasswordParams) => { + return resultify( + axios.post(API_URL_USER.userChangeCancelPassword(), params), + ); +}; + +export const useUserChangeCancelPasswordMutation = (options?: UseMutationOptions) => { + const mutation = useMutation({ + ...options, + mutationFn: (params: ChangeCancelPasswordParams) => userChangeCancelPassword(params), + }); + + return { + ...mutation, + }; +}; diff --git a/src/entities/user/model/types.ts b/src/entities/user/model/types.ts index b52e717..fcfbde0 100644 --- a/src/entities/user/model/types.ts +++ b/src/entities/user/model/types.ts @@ -133,9 +133,9 @@ export interface UserMenuPermissionData { } export interface ChangePasswordParams { - mid: string; usrid: string; password: string; + newPassword: string; } export interface ChangePasswordResponse { @@ -143,6 +143,16 @@ export interface ChangePasswordResponse { error?: ErrorResponse; } +export interface ChangeCancelPasswordParams { + mid: string; + password: string; +} + +export interface ChangeCancelPasswordResponse { + status: boolean; + error?: ErrorResponse; +} + export interface VerificationsItem { type: string; contact: string; diff --git a/src/pages/account/account-pages.tsx b/src/pages/account/account-pages.tsx index 775f442..12c17dc 100644 --- a/src/pages/account/account-pages.tsx +++ b/src/pages/account/account-pages.tsx @@ -8,6 +8,7 @@ import { UserMenuAuthPage } from './user/menu-auth-page'; import { UserAddAccountPage } from './user/add-account-page'; import { PasswordManagePage } from './password/manage-page'; import { PasswordModifyLoginPasswordPage } from './password/modify-login-password-page'; +import { PasswordModifyCancelPasswordPage } from './password/modify-cancel-password-page'; export const AccountPages = () => { @@ -24,6 +25,7 @@ export const AccountPages = () => { } /> } /> + } /> diff --git a/src/pages/account/password/modify-cancel-password-page.tsx b/src/pages/account/password/modify-cancel-password-page.tsx new file mode 100644 index 0000000..f2d8f21 --- /dev/null +++ b/src/pages/account/password/modify-cancel-password-page.tsx @@ -0,0 +1,121 @@ +import { useState } from 'react'; +import { PATHS } from '@/shared/constants/paths'; +import { useNavigate } from '@/shared/lib/hooks/use-navigate'; +import { HeaderType } from '@/entities/common/model/types'; +import { + useSetHeaderTitle, + useSetHeaderType, + useSetFooterMode, + useSetOnBack +} from '@/widgets/sub-layout/use-sub-layout'; +import { useUserChangeCancelPasswordMutation } from '@/entities/user/api/use-user-change-cancel-password-mutation'; +import { snackBar } from '@/shared/lib/toast'; + +export const PasswordModifyCancelPasswordPage = () => { + const { navigate } = useNavigate(); + const [mid, setMid] = useState('nictest00m'); + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + + const changeCancelPasswordMutation = useUserChangeCancelPasswordMutation({ + onSuccess: () => { + snackBar('๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + // Clear form + setNewPassword(''); + setConfirmPassword(''); + // Navigate back + navigate(PATHS.account.password.manage); + }, + onError: (error) => { + snackBar(error?.response?.data?.message || '๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.'); + } + }); + + const midList = [ + { value: 'nictest00m', label: 'nictest00m' }, + { value: 'nictest01m', label: 'nictest01m' }, + { value: 'nictest02m', label: 'nictest02m' }, + ]; + + useSetHeaderTitle('๊ฑฐ๋ž˜์ทจ์†Œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ'); + useSetHeaderType(HeaderType.LeftArrow); + useSetFooterMode(false); + useSetOnBack(() => { + navigate(PATHS.account.password.manage); + }); + + // ์ €์žฅ ๋ฒ„ํŠผ ํ™œ์„ฑํ™” ์กฐ๊ฑด ์ฒดํฌ + const isFormValid = () => { + return ( + newPassword.length >= 8 && + confirmPassword.length >= 8 && + newPassword === confirmPassword + ); + }; + + // ์ €์žฅ ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ + const handleSave = () => { + if (!isFormValid()) return; + + // TODO: Validate current password before submitting + changeCancelPasswordMutation.mutate({ + mid, + password: newPassword + }); + }; + + return ( + <> +
+
+
+
+
+
+
๊ฐ€๋งน์  *
+ +
+
+
๋ณ€๊ฒฝ ๋น„๋ฐ€๋ฒˆํ˜ธ *
+ setNewPassword(e.target.value)} + /> +
+
+
๋ณ€๊ฒฝ ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์ž…๋ ฅ *
+ setConfirmPassword(e.target.value)} + /> +
+ {confirmPassword && newPassword !== confirmPassword && ( +
์ž…๋ ฅ ์ •๋ณด ๋ถˆ์ผ์น˜
+ )} +
+ +
+ +
+
+
+
+
+ + ); +}; \ No newline at end of file diff --git a/src/pages/account/password/modify-login-password-page.tsx b/src/pages/account/password/modify-login-password-page.tsx index d58ec2a..8c069f1 100644 --- a/src/pages/account/password/modify-login-password-page.tsx +++ b/src/pages/account/password/modify-login-password-page.tsx @@ -13,11 +13,10 @@ import { snackBar } from '@/shared/lib/toast'; export const PasswordModifyLoginPasswordPage = () => { const { navigate } = useNavigate(); - const [mid, setMid] = useState('nictest00m'); const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); - const [usrid] = useState(''); // TODO: Get actual user ID from context/session + const [usrid, ] = useState('nictest00'); const changePasswordMutation = useUserChangePasswordMutation({ onSuccess: () => { @@ -34,12 +33,6 @@ export const PasswordModifyLoginPasswordPage = () => { } }); - const midList = [ - { value: 'nictest00m', label: 'nictest00m' }, - { value: 'nictest01m', label: 'nictest01m' }, - { value: 'nictest02m', label: 'nictest02m' }, - ]; - useSetHeaderTitle('๋กœ๊ทธ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ'); useSetHeaderType(HeaderType.LeftArrow); useSetFooterMode(false); @@ -63,9 +56,9 @@ export const PasswordModifyLoginPasswordPage = () => { // TODO: Validate current password before submitting changePasswordMutation.mutate({ - mid, - usrid: usrid || 'test', // TODO: Get actual user ID - password: newPassword + usrid: usrid, + password: newPassword, + newPassword: newPassword, }); }; @@ -76,14 +69,6 @@ export const PasswordModifyLoginPasswordPage = () => {
-
-
๊ฐ€๋งน์  *
- -
๊ธฐ์กด ๋น„๋ฐ€๋ฒˆํ˜ธ *
{ const { navigate } = useNavigate(); const location = useLocation(); const { mid } = location.state || {}; - const { mutateAsync: userCreate, isPending } = useUserCreateMutation(); + + const { mutateAsync: userCreate, isPending } = useUserCreateMutation({ + onSuccess: () => { + snackBar('์‚ฌ์šฉ์ž๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + }, + onError: (error) => { + snackBar(error?.response?.data?.message || '์‚ฌ์šฉ์ž ์ถ”๊ฐ€์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.'); + } + }); // ํผ ์ƒํƒœ ๊ด€๋ฆฌ const [formData, setFormData] = useState({ @@ -373,6 +382,7 @@ export const UserAddAccountPage = () => { if (response.status) { // ์„ฑ๊ณต ์‹œ ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ ํŽ˜์ด์ง€๋กœ ์ด๋™ + snackBar('์‚ฌ์šฉ์ž๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); navigate(PATHS.account.user.manage); } else if (response.error) { // ์—๋Ÿฌ ์ฒ˜๋ฆฌ diff --git a/src/pages/account/user/menu-auth-page.tsx b/src/pages/account/user/menu-auth-page.tsx index 427ad45..4d65047 100644 --- a/src/pages/account/user/menu-auth-page.tsx +++ b/src/pages/account/user/menu-auth-page.tsx @@ -12,6 +12,7 @@ import { useLocation } from 'react-router'; import { useUserMenuPermissionsSaveMutation } from '@/entities/user/api/use-user-menu-permission-save-mutation'; // import { useUserMenuPermissionsMutation } from '@/entities/user/api/use-user-menu-permission-mutation'; import { UserMenuPermissionData } from '@/entities/user/model/types'; +import { snackBar } from '@/shared/lib/toast'; // ๊ถŒํ•œ ๋น„ํŠธ ํ”Œ๋ž˜๊ทธ (์‹ค์ œ API ๋ฐ์ดํ„ฐ ๊ธฐ์ค€) const PERMISSION = { @@ -30,8 +31,15 @@ export const UserMenuAuthPage = () => { const [permissions, setPermissions] = useState>({}); const [initialPermissions, setInitialPermissions] = useState>({}); const [hasChanges, setHasChanges] = useState(false); - const savePermissionsMutation = useUserMenuPermissionsSaveMutation(); - // const getPermissionsMutation = useUserMenuPermissionsMutation(); + const savePermissionsMutation = useUserMenuPermissionsSaveMutation({ + onSuccess: () => { + snackBar('๊ถŒํ•œ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + navigate(PATHS.account.password.manage); + }, + onError: (error) => { + snackBar(error?.response?.data?.message || '๊ถŒํ•œ ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.'); + } + }); useSetHeaderTitle(menuName); useSetHeaderType(HeaderType.LeftArrow); diff --git a/src/shared/api/api-url-user.ts b/src/shared/api/api-url-user.ts index afb6891..b16aa21 100644 --- a/src/shared/api/api-url-user.ts +++ b/src/shared/api/api-url-user.ts @@ -41,4 +41,7 @@ export const API_URL_USER = { changePassword: () => { return `${API_BASE_URL}/api/v1/${API_URL_KEY}/user/password`; }, + userChangeCancelPassword: () => { + return `${API_BASE_URL}/api/v1/${API_URL_KEY}/user/cancel/change`; + } } \ No newline at end of file diff --git a/src/shared/constants/paths.ts b/src/shared/constants/paths.ts index 71c87b1..4aa9a72 100644 --- a/src/shared/constants/paths.ts +++ b/src/shared/constants/paths.ts @@ -122,6 +122,10 @@ export const PATHS: RouteNamesType = { `${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.password.base}`, ROUTE_NAMES.account.password.modifyLoginPassword, ), + modifyCancelPassword: generatePath( + `${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.password.base}`, + ROUTE_NAMES.account.password.modifyCancelPassword, + ), } }, vatReturn: { diff --git a/src/shared/constants/route-names.ts b/src/shared/constants/route-names.ts index 85157fe..b255db4 100644 --- a/src/shared/constants/route-names.ts +++ b/src/shared/constants/route-names.ts @@ -55,8 +55,9 @@ export const ROUTE_NAMES = { password: { base: '/password/*', manage: 'manage', - modifyLoginPassword: 'modifyLoginPassword' - } + modifyLoginPassword: 'change-login-password', + modifyCancelPassword: 'change-cancel-password' + }, }, vatReturn: { base: '/vat-return/*',