- .env.development.example과 lib/core/config/environment.dart, lib/core/permissions/permission_manager.dart에서 PERMISSION__ 폴백을 view 전용으로 좁히고 기본 정책을 명시적으로 거부하도록 재정비했다 - lib/core/navigation/*, lib/core/routing/app_router.dart, lib/widgets/app_shell.dart, lib/main.dart에서 메뉴 매니페스트·카탈로그를 도입해 /menus 응답을 캐싱하고 라우터·사이드바·Breadcrumb가 동일 menu_code/route_path를 쓰도록 리팩터링했다 - lib/core/permissions/permission_resources.dart와 그룹 권한/메뉴 마스터 모듈을 menu_code 기반 CRUD 및 Catalog 경로 정합성 검사로 전환하고 PermissionSynchronizer·PermissionBootstrapper를 확장했다 - test/helpers/test_permissions.dart, test/widgets/app_shell_test.dart 등 신규 구조를 반영하는 테스트·골든과 doc/frontend_menu_permission_tasks.md 문서를 보강했다
127 lines
2.6 KiB
Dart
127 lines
2.6 KiB
Dart
/// 메뉴 엔티티
|
|
///
|
|
/// - 계층 구조를 표현하기 위해 상위 메뉴 정보를 포함한다.
|
|
/// - presentation/data 레이어 세부 구현에 의존하지 않는다.
|
|
class MenuItem {
|
|
MenuItem({
|
|
this.id,
|
|
required this.menuCode,
|
|
required this.menuName,
|
|
this.parent,
|
|
this.path,
|
|
this.displayOrder,
|
|
this.isActive = true,
|
|
this.isDeleted = false,
|
|
this.note,
|
|
this.createdAt,
|
|
this.updatedAt,
|
|
});
|
|
|
|
/// PK (null 이면 신규 생성)
|
|
final int? id;
|
|
|
|
/// 메뉴 코드 (고유)
|
|
final String menuCode;
|
|
|
|
/// 메뉴명
|
|
final String menuName;
|
|
|
|
/// 상위 메뉴 정보
|
|
final MenuSummary? parent;
|
|
|
|
/// 라우트 경로
|
|
final String? path;
|
|
|
|
/// 표시 순서
|
|
final int? displayOrder;
|
|
|
|
/// 사용 여부
|
|
final bool isActive;
|
|
|
|
/// 소프트 삭제 여부
|
|
final bool isDeleted;
|
|
|
|
/// 비고
|
|
final String? note;
|
|
|
|
/// 생성/수정 일시
|
|
final DateTime? createdAt;
|
|
final DateTime? updatedAt;
|
|
|
|
MenuItem copyWith({
|
|
int? id,
|
|
String? menuCode,
|
|
String? menuName,
|
|
MenuSummary? parent,
|
|
String? path,
|
|
int? displayOrder,
|
|
bool? isActive,
|
|
bool? isDeleted,
|
|
String? note,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
}) {
|
|
return MenuItem(
|
|
id: id ?? this.id,
|
|
menuCode: menuCode ?? this.menuCode,
|
|
menuName: menuName ?? this.menuName,
|
|
parent: parent ?? this.parent,
|
|
path: path ?? this.path,
|
|
displayOrder: displayOrder ?? this.displayOrder,
|
|
isActive: isActive ?? this.isActive,
|
|
isDeleted: isDeleted ?? this.isDeleted,
|
|
note: note ?? this.note,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 상위 메뉴 요약 정보
|
|
class MenuSummary {
|
|
MenuSummary({
|
|
required this.id,
|
|
required this.menuName,
|
|
this.menuCode,
|
|
this.path,
|
|
});
|
|
|
|
final int id;
|
|
final String menuName;
|
|
final String? menuCode;
|
|
final String? path;
|
|
}
|
|
|
|
/// 메뉴 생성/수정 입력 모델
|
|
class MenuInput {
|
|
MenuInput({
|
|
required this.menuCode,
|
|
required this.menuName,
|
|
this.parentMenuId,
|
|
this.path,
|
|
this.displayOrder,
|
|
this.isActive = true,
|
|
this.note,
|
|
});
|
|
|
|
final String menuCode;
|
|
final String menuName;
|
|
final int? parentMenuId;
|
|
final String? path;
|
|
final int? displayOrder;
|
|
final bool isActive;
|
|
final String? note;
|
|
|
|
Map<String, dynamic> toPayload() {
|
|
return {
|
|
'menu_code': menuCode,
|
|
'menu_name': menuName,
|
|
'parent_menu_id': parentMenuId,
|
|
'path': path,
|
|
'display_order': displayOrder,
|
|
'is_active': isActive,
|
|
'note': note,
|
|
};
|
|
}
|
|
}
|