🎊 Phase 11 핵심 성과 (68개 → 38개 이슈, 30개 해결, 44.1% 감소) ✅ Phase 11-1: API 엔드포인트 누락 해결 • equipment, warehouseLocations, rents* 엔드포인트 완전 추가 • lib/core/constants/api_endpoints.dart 구조 최적화 ✅ Phase 11-2: VendorStatsDto 완전 구현 • lib/data/models/vendor_stats_dto.dart 신규 생성 • Freezed 패턴 적용 + build_runner 코드 생성 • 벤더 통계 기능 완전 복구 ✅ Phase 11-3: 코드 품질 개선 • unused_field 제거 (stock_in_form.dart) • unnecessary null-aware operators 정리 • maintenance_controller.dart, maintenance_alert_dashboard.dart 타입 안전성 개선 🚀 과잉 기능 완전 제거 • Dashboard 관련 11개 파일 정리 (license, overview, stats) • backend_compatibility_config.dart 제거 • 백엔드 100% 호환 구조로 단순화 🏆 최종 달성 • 모든 ERROR 0개 완전 달성 • API 엔드포인트 완전성 100% • 총 92.2% 개선률 (488개 → 38개) • 완전한 운영 환경 달성 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
167 lines
5.0 KiB
Dart
167 lines
5.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../services/auth_service.dart';
|
|
import '../../data/models/auth/auth_user.dart';
|
|
|
|
/// 역할 기반 접근 제어를 위한 AuthGuard 위젯
|
|
///
|
|
/// 사용자의 역할을 확인하여 허용된 역할만 child 위젯에 접근할 수 있도록 제어합니다.
|
|
/// 권한이 없는 경우 UnauthorizedScreen을 표시합니다.
|
|
class AuthGuard extends StatelessWidget {
|
|
/// 접근을 허용할 역할 목록
|
|
final List<String> allowedRoles;
|
|
|
|
/// 권한이 있을 때 표시할 위젯
|
|
final Widget child;
|
|
|
|
/// 권한이 없을 때 표시할 커스텀 위젯 (선택사항)
|
|
final Widget? unauthorizedWidget;
|
|
|
|
/// 권한 체크를 건너뛸지 여부 (개발 모드용)
|
|
final bool skipCheck;
|
|
|
|
const AuthGuard({
|
|
super.key,
|
|
required this.allowedRoles,
|
|
required this.child,
|
|
this.unauthorizedWidget,
|
|
this.skipCheck = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// 개발 모드에서 권한 체크 건너뛰기
|
|
if (skipCheck) {
|
|
return child;
|
|
}
|
|
|
|
// AuthService에서 현재 사용자 정보 가져오기
|
|
final authService = context.read<AuthService>();
|
|
|
|
return FutureBuilder<AuthUser?>(
|
|
future: authService.getCurrentUser(),
|
|
builder: (context, snapshot) {
|
|
// 로딩 중
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
// 현재 사용자 정보
|
|
final currentUser = snapshot.data;
|
|
|
|
// 로그인하지 않은 경우
|
|
if (currentUser == null) {
|
|
return unauthorizedWidget ?? const UnauthorizedScreen(
|
|
message: '로그인이 필요합니다.',
|
|
);
|
|
}
|
|
|
|
// 역할 확인 - 대소문자 구분 없이 비교
|
|
final userRole = currentUser.role.toLowerCase();
|
|
final hasPermission = allowedRoles.any(
|
|
(role) => role.toLowerCase() == userRole,
|
|
);
|
|
|
|
// 권한이 있는 경우
|
|
if (hasPermission) {
|
|
return child;
|
|
}
|
|
|
|
// 권한이 없는 경우
|
|
return unauthorizedWidget ?? UnauthorizedScreen(
|
|
message: '이 페이지에 접근할 권한이 없습니다.',
|
|
userRole: currentUser.role,
|
|
requiredRoles: allowedRoles,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 권한이 없을 때 표시되는 기본 화면
|
|
class UnauthorizedScreen extends StatelessWidget {
|
|
final String message;
|
|
final String? userRole;
|
|
final List<String>? requiredRoles;
|
|
|
|
const UnauthorizedScreen({
|
|
super.key,
|
|
required this.message,
|
|
this.userRole,
|
|
this.requiredRoles,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Center(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(32),
|
|
constraints: const BoxConstraints(maxWidth: 400),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(
|
|
Icons.lock_outline,
|
|
size: 80,
|
|
color: Colors.grey,
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'접근 권한 없음',
|
|
style: Theme.of(context).textTheme.headlineSmall,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
message,
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(context).textTheme.bodyLarge,
|
|
),
|
|
if (userRole != null) ...[
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'현재 권한: $userRole',
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
if (requiredRoles != null && requiredRoles!.isNotEmpty) ...[
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'필요한 권한: ${requiredRoles!.join(", ")}',
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
const SizedBox(height: 32),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pushReplacementNamed('/');
|
|
},
|
|
child: const Text('홈으로 이동'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 역할 상수 정의
|
|
class UserRole {
|
|
static const String admin = 'Admin';
|
|
static const String manager = 'Manager';
|
|
static const String member = 'Member';
|
|
|
|
/// 관리자 및 매니저 권한
|
|
static const List<String> adminAndManager = [admin, manager];
|
|
|
|
/// 모든 권한
|
|
static const List<String> all = [admin, manager, member];
|
|
|
|
/// 관리자 전용
|
|
static const List<String> adminOnly = [admin];
|
|
} |