feat: 사용자 관리 시스템 백엔드 API 호환성 대폭 개선
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

- UserRemoteDataSource: API v0.2.1 스펙 완전 대응
  • 응답 형식 통일 (success: true 구조)
  • 페이지네이션 처리 개선
  • 에러 핸들링 강화
  • 불필요한 파라미터 제거 (includeInactive 등)

- UserDto 모델 현대화:
  • 서버 응답 구조와 100% 일치
  • 도메인 모델 변환 메서드 추가
  • Freezed 불변성 패턴 완성

- User 도메인 모델 신규 구현:
  • Clean Architecture 원칙 준수
  • UserRole enum 타입 안전성 강화
  • 비즈니스 로직 캡슐화

- 사용자 관련 UseCase 리팩토링:
  • Repository 패턴 완전 적용
  • Either<Failure, Success> 에러 처리
  • 의존성 주입 최적화

- UI 컨트롤러 및 화면 개선:
  • API 응답 변경사항 반영
  • 사용자 권한 표시 정확성 향상
  • 폼 검증 로직 강화

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-08-15 23:31:51 +09:00
parent c1063f5670
commit 93bceb8a6c
20 changed files with 2006 additions and 2017 deletions

View File

@@ -215,6 +215,8 @@ class _AppLayoutState extends State<AppLayout>
return '장비 관리';
case Routes.company:
return '회사 관리';
case Routes.user:
return '사용자 관리';
case Routes.license:
return '유지보수 관리';
case Routes.warehouseLocation:
@@ -241,6 +243,8 @@ class _AppLayoutState extends State<AppLayout>
return ['', '장비 관리', '대여'];
case Routes.company:
return ['', '회사 관리'];
case Routes.user:
return ['', '사용자 관리'];
case Routes.license:
return ['', '유지보수 관리'];
case Routes.warehouseLocation:
@@ -302,10 +306,10 @@ class _AppLayoutState extends State<AppLayout>
),
child: Column(
children: [
// F-Pattern: 2차 시선 - 페이지 헤더 + 액션
_buildPageHeader(),
const SizedBox(height: ShadcnTheme.spacing4),
// F-Pattern: 2차 시선 - 페이지 헤더 + 액션 (주석처리)
// _buildPageHeader(),
//
// const SizedBox(height: ShadcnTheme.spacing4),
// F-Pattern: 주요 작업 영역
Expanded(
@@ -560,12 +564,14 @@ class _AppLayoutState extends State<AppLayout>
);
}
/// F-Pattern 2차 시선: 페이지 헤더 (제목 + 주요 액션 버튼)
/// F-Pattern 2차 시선: 페이지 헤더 (간소화된 제목)
Widget _buildPageHeader() {
final breadcrumbs = _getBreadcrumbs();
final breadcrumbs = _getBreadcrumbs(); // 변수는 유지 (향후 사용 가능)
return Container(
padding: const EdgeInsets.all(ShadcnTheme.spacing6),
// BaseListScreen과 동일한 폭을 위해 마진 추가
margin: const EdgeInsets.symmetric(horizontal: ShadcnTheme.spacing6), // 24px 마진 추가
padding: const EdgeInsets.all(ShadcnTheme.spacing3), // 12px 내부 패딩
decoration: BoxDecoration(
color: ShadcnTheme.background,
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg),
@@ -574,82 +580,51 @@ class _AppLayoutState extends State<AppLayout>
width: 1,
),
),
child: Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 왼쪽: 페이지 제목 + 브레드크럼
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 페이지 제목
Text(
_getPageTitle(),
style: ShadcnTheme.headingH4,
),
const SizedBox(height: ShadcnTheme.spacing1),
// 브레드크럼
Row(
children: [
for (int i = 0; i < breadcrumbs.length; i++) ...[
if (i > 0) ...[
const SizedBox(width: ShadcnTheme.spacing1),
Icon(
Icons.chevron_right,
size: 14,
color: ShadcnTheme.foregroundSubtle,
),
const SizedBox(width: ShadcnTheme.spacing1),
],
Text(
breadcrumbs[i],
style: i == breadcrumbs.length - 1
? ShadcnTheme.bodySmall.copyWith(
color: ShadcnTheme.foreground,
fontWeight: FontWeight.w500,
)
: ShadcnTheme.bodySmall.copyWith(
color: ShadcnTheme.foregroundMuted,
),
),
],
],
),
],
// 페이지 제목 (좌측 패딩 + 작은 텍스트)
Padding(
padding: const EdgeInsets.only(left: ShadcnTheme.spacing2), // 좌측 패딩 추가
child: Text(
_getPageTitle(),
style: ShadcnTheme.bodySmall.copyWith(
fontWeight: FontWeight.w500, // 약간 두껴게
),
),
),
// 오른쪽: 페이지별 주요 액션 버튼들
if (_currentRoute != Routes.home) ...[
Row(
children: [
// 새로고침
ShadcnButton(
text: '',
icon: Icon(Icons.refresh, size: 18),
onPressed: () {
// 페이지 새로고침
setState(() {});
_loadLicenseExpirySummary(); // 라이선스 만료 정보도 새로고침
},
variant: ShadcnButtonVariant.ghost,
size: ShadcnButtonSize.small,
),
const SizedBox(width: ShadcnTheme.spacing2),
// 추가 버튼 (리스트 페이지에서만)
if (_isListPage()) ...[
ShadcnButton(
text: '추가',
icon: Icon(Icons.add, size: 18),
onPressed: () {
_handleAddAction();
},
variant: ShadcnButtonVariant.primary,
size: ShadcnButtonSize.small,
// 브레드크럼 (주석처리)
/*
const SizedBox(height: ShadcnTheme.spacing1),
// 브레드크럼
Row(
children: [
for (int i = 0; i < breadcrumbs.length; i++) ...[
if (i > 0) ...[
const SizedBox(width: ShadcnTheme.spacing1),
Icon(
Icons.chevron_right,
size: 14,
color: ShadcnTheme.foregroundSubtle,
),
const SizedBox(width: ShadcnTheme.spacing1),
],
Text(
breadcrumbs[i],
style: i == breadcrumbs.length - 1
? ShadcnTheme.bodySmall.copyWith(
color: ShadcnTheme.foreground,
fontWeight: FontWeight.w500,
)
: ShadcnTheme.bodySmall.copyWith(
color: ShadcnTheme.foregroundMuted,
),
),
],
),
],
],
),
*/
],
),
);
@@ -663,6 +638,7 @@ class _AppLayoutState extends State<AppLayout>
Routes.equipmentOutList,
Routes.equipmentRentList,
Routes.company,
Routes.user,
Routes.license,
Routes.warehouseLocation,
].contains(_currentRoute);
@@ -682,6 +658,9 @@ class _AppLayoutState extends State<AppLayout>
case Routes.company:
addRoute = '/company/add';
break;
case Routes.user:
addRoute = '/user/add';
break;
case Routes.license:
addRoute = '/license/add';
break;
@@ -960,6 +939,14 @@ class SidebarMenu extends StatelessWidget {
badge: null,
),
_buildMenuItem(
icon: Icons.people_outlined,
title: '사용자 관리',
route: Routes.user,
isActive: currentRoute == Routes.user,
badge: null,
),
_buildMenuItem(
icon: Icons.support_outlined,
title: '유지보수 관리',
@@ -1129,6 +1116,8 @@ class SidebarMenu extends StatelessWidget {
return Icons.warehouse;
case Icons.business_outlined:
return Icons.business;
case Icons.people_outlined:
return Icons.people;
case Icons.support_outlined:
return Icons.support;
case Icons.bug_report_outlined: