- 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>
301 lines
11 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}; |