사용하지 않는 파일 정리 전 백업 (Phase 10 완료 후 상태)
This commit is contained in:
@@ -1,21 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'dart:async';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport/core/constants/app_constants.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/company_item_model.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/screens/common/components/shadcn_components.dart';
|
||||
import 'package:superport/screens/common/widgets/pagination.dart';
|
||||
import 'package:superport/screens/common/widgets/unified_search_bar.dart';
|
||||
import 'package:superport/screens/common/widgets/standard_data_table.dart'
|
||||
as std_table;
|
||||
import 'package:superport/screens/common/widgets/standard_action_bar.dart';
|
||||
import 'package:superport/screens/common/widgets/standard_states.dart';
|
||||
import 'package:superport/screens/common/layouts/base_list_screen.dart';
|
||||
// import 'package:superport/services/mock_data_service.dart'; // Mock 서비스 제거
|
||||
import 'package:superport/screens/company/widgets/company_branch_dialog.dart';
|
||||
import 'package:superport/screens/company/controllers/company_list_controller.dart';
|
||||
import 'package:superport/screens/company/components/company_tree_view.dart';
|
||||
|
||||
/// shadcn/ui 스타일로 재설계된 회사 관리 화면 (통일된 UI 컴포넌트 사용)
|
||||
class CompanyList extends StatefulWidget {
|
||||
@@ -73,127 +70,97 @@ class _CompanyListState extends State<CompanyList> {
|
||||
void _deleteCompany(int id) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('삭제 확인'),
|
||||
content: const Text('이 회사 정보를 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
try {
|
||||
await _controller.deleteCompany(id);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toString()),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
builder: (context) => ShadDialog(
|
||||
title: const Text('삭제 확인'),
|
||||
description: const Text('이 회사 정보를 삭제하시겠습니까?'),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
try {
|
||||
await _controller.deleteCompany(id);
|
||||
if (mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('성공'),
|
||||
description: Text('회사가 삭제되었습니다.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
),
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast.destructive(
|
||||
title: const Text('오류'),
|
||||
description: Text(e.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 지점 다이얼로그 표시
|
||||
void _showBranchDialog(Company mainCompany) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => CompanyBranchDialog(mainCompany: mainCompany),
|
||||
);
|
||||
}
|
||||
|
||||
/// 지점 삭제 처리
|
||||
void _deleteBranch(int companyId, int branchId) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
builder: (context) => ShadDialog(
|
||||
title: const Text('지점 삭제 확인'),
|
||||
content: const Text('이 지점 정보를 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
try {
|
||||
await _controller.deleteBranch(companyId, branchId);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toString()),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
description: const Text('이 지점 정보를 삭제하시겠습니까?'),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
try {
|
||||
await _controller.deleteBranch(companyId, branchId);
|
||||
if (mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('성공'),
|
||||
description: Text('지점이 삭제되었습니다.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast.destructive(
|
||||
title: const Text('오류'),
|
||||
description: Text(e.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
},
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 회사 유형 배지 생성
|
||||
Widget _buildCompanyTypeChips(List<CompanyType> types) {
|
||||
// 유형이 없으면 기본값 표시
|
||||
if (types.isEmpty) {
|
||||
return Text(
|
||||
'-',
|
||||
style: ShadcnTheme.bodySmall.copyWith(color: ShadcnTheme.muted),
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 2,
|
||||
children: types.map((type) {
|
||||
ShadcnBadgeVariant variant;
|
||||
String displayText;
|
||||
|
||||
switch (type) {
|
||||
case CompanyType.customer:
|
||||
variant = ShadcnBadgeVariant.companyCustomer; // Orange
|
||||
displayText = '고객사';
|
||||
break;
|
||||
case CompanyType.partner:
|
||||
variant = ShadcnBadgeVariant.companyPartner; // Green
|
||||
displayText = '파트너사';
|
||||
break;
|
||||
default:
|
||||
variant = ShadcnBadgeVariant.secondary;
|
||||
displayText = companyTypeToString(type);
|
||||
}
|
||||
|
||||
return ShadcnBadge(
|
||||
text: displayText,
|
||||
variant: variant,
|
||||
size: ShadcnBadgeSize.small,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 본사/지점 구분 배지 생성
|
||||
Widget _buildCompanyTypeLabel(bool isBranch) {
|
||||
@@ -363,6 +330,174 @@ class _CompanyListState extends State<CompanyList> {
|
||||
return Text(created, style: ShadcnTheme.bodySmall);
|
||||
}
|
||||
}
|
||||
|
||||
/// ShadTable을 사용한 회사 데이터 테이블 빌드
|
||||
Widget _buildCompanyShadTable(List<CompanyItem> items, CompanyListController controller) {
|
||||
final theme = ShadTheme.of(context);
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: ShadTable(
|
||||
columnCount: 11,
|
||||
rowCount: items.length + 1, // +1 for header
|
||||
header: (context, column) {
|
||||
final headers = [
|
||||
'번호', '회사명', '구분', '주소', '담당자',
|
||||
'연락처', '파트너/고객', '상태', '등록/수정일', '비고', '관리'
|
||||
];
|
||||
return ShadTableCell(
|
||||
child: Text(
|
||||
headers[column],
|
||||
style: theme.textTheme.muted.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
},
|
||||
builder: (context, vicinity) {
|
||||
final column = vicinity.column;
|
||||
final row = vicinity.row - 1; // -1 because header is row 0
|
||||
|
||||
if (row < 0 || row >= items.length) {
|
||||
return const ShadTableCell(child: SizedBox.shrink());
|
||||
}
|
||||
|
||||
final item = items[row];
|
||||
final index = ((controller.currentPage - 1) * controller.pageSize) + row;
|
||||
|
||||
switch (column) {
|
||||
case 0: // 번호
|
||||
return ShadTableCell(child: Text('${index + 1}', style: theme.textTheme.small));
|
||||
case 1: // 회사명
|
||||
return ShadTableCell(child: _buildDisplayNameText(item));
|
||||
case 2: // 구분
|
||||
return ShadTableCell(child: _buildCompanyTypeLabel(item.isBranch));
|
||||
case 3: // 주소
|
||||
return ShadTableCell(
|
||||
child: Text(
|
||||
item.address.isNotEmpty ? item.address : '-',
|
||||
style: theme.textTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
case 4: // 담당자
|
||||
return ShadTableCell(child: _buildContactInfo(item));
|
||||
case 5: // 연락처
|
||||
return ShadTableCell(child: _buildContactDetails(item));
|
||||
case 6: // 파트너/고객
|
||||
return ShadTableCell(child: _buildPartnerCustomerFlags(item));
|
||||
case 7: // 상태
|
||||
return ShadTableCell(child: _buildStatusBadge(item.isActive));
|
||||
case 8: // 등록/수정일
|
||||
return ShadTableCell(child: _buildDateInfo(item));
|
||||
case 9: // 비고
|
||||
return ShadTableCell(
|
||||
child: Text(
|
||||
item.remark ?? '-',
|
||||
style: theme.textTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
),
|
||||
);
|
||||
case 10: // 관리
|
||||
return ShadTableCell(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (item.id != null) ...[
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, size: 18),
|
||||
onPressed: () {
|
||||
if (item.isBranch) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/branch/edit',
|
||||
arguments: {
|
||||
'companyId': item.parentCompanyId,
|
||||
'branchId': item.id,
|
||||
'parentCompanyName': item.parentCompanyName,
|
||||
},
|
||||
).then((result) {
|
||||
if (result == true) controller.refresh();
|
||||
});
|
||||
} else {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/edit',
|
||||
arguments: {
|
||||
'companyId': item.id,
|
||||
'isBranch': false,
|
||||
},
|
||||
).then((result) {
|
||||
if (result == true) controller.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete, size: 18),
|
||||
onPressed: () {
|
||||
if (item.isBranch) {
|
||||
_deleteBranch(item.parentCompanyId!, item.id!);
|
||||
} else {
|
||||
_deleteCompany(item.id!);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
default:
|
||||
return const ShadTableCell(child: SizedBox.shrink());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Tree View 빌드
|
||||
Widget _buildTreeView(BuildContext context, CompanyListController controller) {
|
||||
if (controller.companyHierarchy == null) {
|
||||
return const StandardLoadingState(message: '계층 구조를 불러오는 중...');
|
||||
}
|
||||
|
||||
return CompanyTreeView(
|
||||
hierarchy: controller.companyHierarchy!,
|
||||
expandedNodes: controller.expandedNodes,
|
||||
onToggleExpand: controller.toggleNodeExpansion,
|
||||
onNodeTap: (nodeId) {
|
||||
// 회사 상세 화면으로 이동
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/edit',
|
||||
arguments: int.parse(nodeId),
|
||||
);
|
||||
},
|
||||
onEdit: (nodeId) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/edit',
|
||||
arguments: int.parse(nodeId),
|
||||
);
|
||||
},
|
||||
onDelete: (nodeId) async {
|
||||
final companyId = int.parse(nodeId);
|
||||
// 삭제 가능 여부 먼저 확인
|
||||
final canDelete = await controller.canDeleteCompany(companyId);
|
||||
if (canDelete) {
|
||||
_deleteCompany(companyId);
|
||||
} else {
|
||||
if (mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('삭제 불가'),
|
||||
description: Text('자식 회사가 있어 삭제할 수 없습니다.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -434,6 +569,16 @@ class _CompanyListState extends State<CompanyList> {
|
||||
),
|
||||
],
|
||||
rightActions: [
|
||||
// Tree View 토글 버튼
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
controller.isTreeView ? Icons.list : Icons.account_tree,
|
||||
color: controller.isTreeView ? ShadcnTheme.primary : null,
|
||||
),
|
||||
tooltip: controller.isTreeView ? '리스트 보기' : '계층 보기',
|
||||
onPressed: () => controller.toggleTreeView(),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// 관리자용 비활성 포함 체크박스
|
||||
// TODO: 실제 권한 체크 로직 추가 필요
|
||||
Row(
|
||||
@@ -451,7 +596,7 @@ class _CompanyListState extends State<CompanyList> {
|
||||
statusMessage: controller.searchQuery.isNotEmpty
|
||||
? '"${controller.searchQuery}" 검색 결과'
|
||||
: actualHeadquartersCount > 0
|
||||
? '본사: ${actualHeadquartersCount}개, 지점: ${actualBranchesCount}개 총 ${totalCount}개'
|
||||
? '본사: $actualHeadquartersCount개, 지점: $actualBranchesCount개 총 $totalCount개'
|
||||
: null,
|
||||
),
|
||||
|
||||
@@ -466,9 +611,10 @@ class _CompanyListState extends State<CompanyList> {
|
||||
)
|
||||
: null,
|
||||
|
||||
// 데이터 테이블
|
||||
dataTable:
|
||||
companyItems.isEmpty
|
||||
// 데이터 테이블 또는 Tree View
|
||||
dataTable: controller.isTreeView
|
||||
? _buildTreeView(context, controller)
|
||||
: companyItems.isEmpty
|
||||
? StandardEmptyState(
|
||||
title:
|
||||
controller.searchQuery.isNotEmpty
|
||||
@@ -483,136 +629,7 @@ class _CompanyListState extends State<CompanyList> {
|
||||
)
|
||||
: null,
|
||||
)
|
||||
: std_table.StandardDataTable(
|
||||
columns: [
|
||||
std_table.DataColumn(label: '번호', flex: 1),
|
||||
std_table.DataColumn(label: '회사명', flex: 3),
|
||||
std_table.DataColumn(label: '구분', flex: 1),
|
||||
std_table.DataColumn(label: '주소', flex: 3),
|
||||
std_table.DataColumn(label: '담당자', flex: 2),
|
||||
std_table.DataColumn(label: '연락처', flex: 3),
|
||||
std_table.DataColumn(label: '파트너/고객', flex: 2),
|
||||
std_table.DataColumn(label: '상태', flex: 1),
|
||||
std_table.DataColumn(label: '등록/수정일', flex: 2),
|
||||
std_table.DataColumn(label: '비고', flex: 2),
|
||||
std_table.DataColumn(label: '관리', flex: 2),
|
||||
],
|
||||
rows: [
|
||||
...companyItems.asMap().entries.map((entry) {
|
||||
final int index = ((controller.currentPage - 1) * controller.pageSize) + entry.key;
|
||||
final CompanyItem item = entry.value;
|
||||
|
||||
return std_table.StandardDataRow(
|
||||
index: index,
|
||||
columns: [
|
||||
std_table.DataColumn(label: '번호', flex: 1),
|
||||
std_table.DataColumn(label: '회사명', flex: 3),
|
||||
std_table.DataColumn(label: '구분', flex: 1),
|
||||
std_table.DataColumn(label: '주소', flex: 3),
|
||||
std_table.DataColumn(label: '담당자', flex: 2),
|
||||
std_table.DataColumn(label: '연락처', flex: 3),
|
||||
std_table.DataColumn(label: '파트너/고객', flex: 2),
|
||||
std_table.DataColumn(label: '상태', flex: 1),
|
||||
std_table.DataColumn(label: '등록/수정일', flex: 2),
|
||||
std_table.DataColumn(label: '비고', flex: 2),
|
||||
std_table.DataColumn(label: '관리', flex: 2),
|
||||
],
|
||||
cells: [
|
||||
// 번호
|
||||
Text(
|
||||
'${index + 1}',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
// 회사명 (계층적 표시)
|
||||
_buildDisplayNameText(item),
|
||||
// 구분 (본사/지점 배지)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _buildCompanyTypeLabel(item.isBranch),
|
||||
),
|
||||
// 주소
|
||||
Text(
|
||||
item.address.isNotEmpty ? item.address : '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
// 담당자 (이름 + 직책)
|
||||
_buildContactInfo(item),
|
||||
// 연락처 (전화 + 이메일)
|
||||
_buildContactDetails(item),
|
||||
// 파트너/고객 플래그
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _buildPartnerCustomerFlags(item),
|
||||
),
|
||||
// 상태
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _buildStatusBadge(item.isActive),
|
||||
),
|
||||
// 등록/수정일
|
||||
_buildDateInfo(item),
|
||||
// 비고
|
||||
Text(
|
||||
item.remark ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
),
|
||||
// 관리 (액션 버튼들)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
std_table.StandardActionButtons(
|
||||
onEdit: item.id != null
|
||||
? () {
|
||||
if (item.isBranch) {
|
||||
// 지점 수정 - 통합 지점 관리 화면으로 이동
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/branch/edit',
|
||||
arguments: {
|
||||
'companyId': item.parentCompanyId,
|
||||
'branchId': item.id,
|
||||
'parentCompanyName': item.parentCompanyName,
|
||||
},
|
||||
).then((result) {
|
||||
if (result == true) controller.refresh();
|
||||
});
|
||||
} else {
|
||||
// 본사 수정
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/company/edit',
|
||||
arguments: {
|
||||
'companyId': item.id,
|
||||
'isBranch': false,
|
||||
},
|
||||
).then((result) {
|
||||
if (result == true) controller.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
: null,
|
||||
onDelete: item.id != null
|
||||
? () {
|
||||
if (item.isBranch) {
|
||||
// 지점 삭제
|
||||
_deleteBranch(item.parentCompanyId!, item.id!);
|
||||
} else {
|
||||
// 본사 삭제
|
||||
_deleteCompany(item.id!);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
: _buildCompanyShadTable(companyItems, controller),
|
||||
|
||||
// 페이지네이션 (BaseListController의 goToPage 사용)
|
||||
pagination: Pagination(
|
||||
|
||||
Reference in New Issue
Block a user