Files
nice-app-web/src/pages/account/user/menu-auth-page.tsx
Jay Sheen 79271caab3 refactor: Update account authentication and password input components
- Replace XKeypad with standard password input fields
- Update user authentication and login info components
- Modify password modification and account pages
- Add sub-layout widget enhancements

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 10:34:28 +09:00

301 lines
11 KiB
TypeScript

import { useEffect, 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 { 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 = {
READ: 1, // 조회
SAVE: 2, // 저장
EXECUTE: 4, // 실행
DOWNLOAD: 8 // 다운로드
};
export const UserMenuAuthPage = () => {
const { navigate } = useNavigate();
const location = useLocation();
const { mid, usrid, menuName, subMenu, menuGrants } = location.state || {};
// 메뉴별 권한 상태 관리
const [permissions, setPermissions] = useState<Record<number, number>>({});
const [initialPermissions, setInitialPermissions] = useState<Record<number, number>>({});
const [defaultPermissions, setDefaultPermissions] = useState<Record<number, number>>({});
const [hasChanges, setHasChanges] = useState(false);
const [isInitialLoad, setIsInitialLoad] = useState(true);
const savePermissionsMutation = useUserMenuPermissionsSaveMutation({
onSuccess: () => {
snackBar('권한이 성공적으로 저장되었습니다.');
navigate(PATHS.account.user.accountAuth, {
state: {
mid,
usrid,
idCl: location.state?.idCl,
status: location.state?.status
}
});
},
onError: (error) => {
snackBar(error?.response?.data?.message || '권한 저장에 실패했습니다.');
}
});
useSetHeaderTitle(menuName);
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.account.user.accountAuth, {
state: {
mid,
usrid,
idCl: location.state?.idCl,
status: location.state?.status
}
});
});
useEffect(() => {
console.log('menuGrants : ', menuGrants);
}, [menuGrants]);
// // 메뉴 권한 조회 함수
// const loadPermissions = useCallback(() => {
// if (mid && usrid) {
// getPermissionsMutation.mutate(
// { mid, usrid },
// {
// onSuccess: (response) => {
// if (response.data) {
// const perms: Record<number, number> = {};
// response.data.forEach((item: UserMenuPermissionData) => {
// perms[item.menuId] = item.grant;
// });
// setPermissions(perms);
// }
// }
// }
// );
// }
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [mid, usrid]);
// menuGrants를 권한 상태로 초기화 또는 API에서 권한 조회
useEffect(() => {
if (menuGrants && Array.isArray(menuGrants) && menuGrants.length > 0) {
// menuGrants 데이터가 있으면 사용
const grants: Record<number, number> = {};
const defaultGrants: Record<number, number> = {};
menuGrants.forEach((grant: { menuId: number; grant: number; defaultGrant: number }) => {
grants[grant.menuId] = grant.grant || 0;
defaultGrants[grant.menuId] = grant.defaultGrant || 0;
});
setPermissions(grants);
setInitialPermissions(grants); // 초기값은 현재 grant 값으로 설정
setDefaultPermissions(defaultGrants); // defaultGrant는 별도로 관리
// 초기 로드 완료 후 애니메이션 활성화
setTimeout(() => setIsInitialLoad(false), 100);
} else {
// menuGrants가 없거나 빈 배열이면 API에서 권한 조회
// loadPermissions();
}
}, [menuGrants]);
// 권한 변경 감지
useEffect(() => {
const hasAnyChange = Object.keys(permissions).some(key => {
const menuId = Number(key);
return permissions[menuId] !== (initialPermissions[menuId] || 0);
}) || Object.keys(initialPermissions).some(key => {
const menuId = Number(key);
return (permissions[menuId] || 0) !== initialPermissions[menuId];
});
setHasChanges(hasAnyChange);
}, [permissions, initialPermissions]);
// 특정 메뉴의 권한 체크
const hasPermission = (menuId: number, flag: number): boolean => {
const grant = permissions[menuId] || 0;
return (grant & flag) === flag;
};
const hasDefaultPermission = (menuId: number, flag: number): boolean => {
const grant = defaultPermissions[menuId] || 0;
return (grant & flag) === flag;
};
// 권한 토글 처리
const togglePermission = (menuId: number, flag: number) => {
setPermissions(prev => {
const currentGrant = prev[menuId] || 0;
const newGrant = currentGrant ^ flag;
return {
...prev,
[menuId]: newGrant
};
});
};
// 메인 토글 처리 (VIEW 권한)
const toggleMainPermission = (menuId: number) => {
setPermissions(prev => {
const currentGrant = prev[menuId] || 0;
if (currentGrant > 0) {
// 권한이 있으면 모두 제거
return {
...prev,
[menuId]: 0
};
} else {
// 권한이 없으면 VIEW 권한만 부여
return {
...prev,
[menuId]: PERMISSION.READ
};
}
});
};
// 권한 저장
const handleSave = () => {
const namsUserMenuAccess: UserMenuPermissionData[] = Object.entries(permissions).map(
([menuId, grant]) => ({
menuId: Number(menuId),
usrid: usrid,
grant: grant,
defaultGrant: defaultPermissions[Number(menuId)] || 0
})
);
savePermissionsMutation.mutate(
{ mid, namsUserMenuAccess },
{
onSuccess: () => {
snackBar('권한이 저장되었습니다.');
// 저장 성공 후 초기값 업데이트
setInitialPermissions({...permissions});
setHasChanges(false);
},
onError: (error) => {
alert('권한 저장에 실패했습니다.');
console.error(error);
}
}
);
};
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane pt-46 active">
<div className="ing-list pb-86">
<div className="desc service-tip"> .</div>
<div className="desc service-tip"> .</div>
{subMenu && subMenu.map((menu: { menuId: number; menuName: string }) => {
const menuGrant = permissions[menu.menuId] || 0;
const hasAccess = menuGrant > 0;
return (
<div key={menu.menuId}>
<div className="settings-section nopadding">
<div className="settings-row">
<div className="settings-row-title bd-style">{menu.menuName}</div>
<label className="settings-switch">
<input
type="checkbox"
checked={hasAccess}
onChange={() => toggleMainPermission(menu.menuId)}
/>
<span className="slider"></span>
</label>
</div>
<div
className="permission-details"
style={{
maxHeight: hasAccess ? '300px' : '0',
opacity: hasAccess ? 1 : 0,
overflow: 'hidden',
transition: isInitialLoad ? 'none' : 'max-height 0.3s ease-in-out, opacity 0.3s ease-in-out'
}}
>
{(hasDefaultPermission(menu.menuId, PERMISSION.SAVE) || hasDefaultPermission(menu.menuId, PERMISSION.EXECUTE) || hasDefaultPermission(menu.menuId, PERMISSION.DOWNLOAD)) && (
<div className="set-divider"></div>
)}
{hasDefaultPermission(menu.menuId, PERMISSION.SAVE) && (
<div className="settings-row">
<span className="settings-row-title bd-sub dot"></span>
<label className="settings-switch">
<input
type="checkbox"
checked={hasPermission(menu.menuId, PERMISSION.SAVE)}
onChange={() => togglePermission(menu.menuId, PERMISSION.SAVE)}
/>
<span className="slider"></span>
</label>
</div>
)}
{hasDefaultPermission(menu.menuId, PERMISSION.EXECUTE) && (
<div className="settings-row">
<span className="settings-row-title bd-sub dot"></span>
<label className="settings-switch">
<input
type="checkbox"
checked={hasPermission(menu.menuId, PERMISSION.EXECUTE)}
onChange={() => togglePermission(menu.menuId, PERMISSION.EXECUTE)}
/>
<span className="slider"></span>
</label>
</div>
)}
{hasDefaultPermission(menu.menuId, PERMISSION.DOWNLOAD) && (
<div className="settings-row">
<span className="settings-row-title bd-sub dot"></span>
<label className="settings-switch">
<input
type="checkbox"
checked={hasPermission(menu.menuId, PERMISSION.DOWNLOAD)}
onChange={() => togglePermission(menu.menuId, PERMISSION.DOWNLOAD)}
/>
<span className="slider"></span>
</label>
</div>
)}
</div>
</div>
<div className="ht-20"></div>
</div>
);
})}
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
type="button"
onClick={handleSave}
disabled={!hasChanges || savePermissionsMutation.isPending}
>
{savePermissionsMutation.isPending ? '저장 중...' : '저장'}
</button>
</div>
</div>
</div>
</main>
</>
);
};