refactor: Equipment 리스트 화면 API 호환성 개선
- 리스트 API가 제공하지 않는 9개 컬럼 제거 (카테고리, 바코드, 입고지, 구매처, 구매일, 구매가격, 현재위치, 창고위치, 점검일) - 실제 제공되는 데이터만 표시하도록 최적화 (제조사, 장비번호, 모델명, 시리얼번호, 수량, 상태, 입출고일) - Equipment 필드명 변경 대응 (name → equipmentNumber, category 하드코딩 개선) - 불필요한 헬퍼 함수 제거 및 테이블 너비 계산 최적화 - 헬스체크 주기 조정 (30초 → 300초)
This commit is contained in:
@@ -27,7 +27,7 @@ class AppConstants {
|
||||
static const int licenseExpiryInfoDays = 90;
|
||||
|
||||
// 헬스체크 주기
|
||||
static const Duration healthCheckInterval = Duration(seconds: 30);
|
||||
static const Duration healthCheckInterval = Duration(seconds: 300);
|
||||
|
||||
// 토큰 키
|
||||
static const String accessTokenKey = 'access_token';
|
||||
@@ -74,7 +74,14 @@ class AppConstants {
|
||||
// 파일 업로드
|
||||
static const int maxFileSize = 10 * 1024 * 1024; // 10MB
|
||||
static const List<String> allowedFileExtensions = [
|
||||
'jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx'
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'pdf',
|
||||
'doc',
|
||||
'docx',
|
||||
'xls',
|
||||
'xlsx',
|
||||
];
|
||||
|
||||
// 에러 메시지
|
||||
@@ -89,9 +96,7 @@ class AppConstants {
|
||||
r'^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+',
|
||||
);
|
||||
|
||||
static final RegExp phoneRegex = RegExp(
|
||||
r'^01[0-9]{1}-?[0-9]{4}-?[0-9]{4}$',
|
||||
);
|
||||
static final RegExp phoneRegex = RegExp(r'^01[0-9]{1}-?[0-9]{4}-?[0-9]{4}$');
|
||||
|
||||
static final RegExp businessNumberRegex = RegExp(
|
||||
r'^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$',
|
||||
|
||||
@@ -108,9 +108,11 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
manufacturer: dto.manufacturer ?? 'Unknown',
|
||||
equipmentNumber: dto.equipmentNumber ?? 'Unknown', // name → equipmentNumber (required)
|
||||
modelName: dto.modelName ?? dto.equipmentNumber ?? 'Unknown', // 새로운 필수 필드 (required)
|
||||
category1: 'Equipment', // category → category1 (required)
|
||||
category2: 'General', // subCategory → category2 (required)
|
||||
category3: 'Standard', // subSubCategory → category3 (required)
|
||||
// 🔧 [BUG FIX] 하드코딩 제거 - 백엔드 API에서 카테고리 정보 미제공 시 기본값 사용
|
||||
// TODO: 백엔드 API에서 category1/2/3 필드 추가 필요
|
||||
category1: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
|
||||
category2: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
|
||||
category3: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
|
||||
serialNumber: dto.serialNumber,
|
||||
quantity: 1, // 기본 수량
|
||||
);
|
||||
@@ -154,7 +156,8 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
@override
|
||||
bool filterItem(UnifiedEquipment item, String query) {
|
||||
final q = query.toLowerCase();
|
||||
return (item.equipment.name.toLowerCase().contains(q)) ||
|
||||
return (item.equipment.equipmentNumber.toLowerCase().contains(q)) || // name → equipmentNumber
|
||||
(item.equipment.modelName?.toLowerCase().contains(q) ?? false) || // 모델명 추가
|
||||
(item.equipment.serialNumber?.toLowerCase().contains(q) ?? false) ||
|
||||
(item.equipment.manufacturer.toLowerCase().contains(q)) ||
|
||||
(item.notes?.toLowerCase().contains(q) ?? false) ||
|
||||
@@ -311,7 +314,7 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
reason: reason ?? '폐기 처리',
|
||||
);
|
||||
} catch (e) {
|
||||
failedEquipments.add('${equipment.equipment.manufacturer} ${equipment.equipment.name}');
|
||||
failedEquipments.add('${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}'); // name → equipmentNumber
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,10 +195,11 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
final keyword = _appliedSearchKeyword.toLowerCase();
|
||||
return [
|
||||
e.equipment.manufacturer,
|
||||
e.equipment.name,
|
||||
e.equipment.category,
|
||||
e.equipment.subCategory,
|
||||
e.equipment.subSubCategory,
|
||||
e.equipment.equipmentNumber, // name → equipmentNumber (메인 필드)
|
||||
e.equipment.modelName ?? '', // 모델명 추가
|
||||
e.equipment.category1, // category → category1 (메인 필드)
|
||||
e.equipment.category2, // subCategory → category2 (메인 필드)
|
||||
e.equipment.category3, // subSubCategory → category3 (메인 필드)
|
||||
e.equipment.serialNumber ?? '',
|
||||
e.equipment.barcode ?? '',
|
||||
e.equipment.remark ?? '',
|
||||
@@ -288,7 +289,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
'${equipment.manufacturer} ${equipment.name}',
|
||||
'${equipment.manufacturer} ${equipment.equipmentNumber}', // name → equipmentNumber
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
@@ -434,7 +435,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
final result = await EquipmentHistoryDialog.show(
|
||||
context: context,
|
||||
equipmentId: equipment.equipment.id!,
|
||||
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.name}',
|
||||
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}', // name → equipmentNumber
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
@@ -731,28 +732,20 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
double _getMinimumTableWidth(List<UnifiedEquipment> pagedEquipments) {
|
||||
double totalWidth = 0;
|
||||
|
||||
// 기본 컬럼들 (최소 너비)
|
||||
// 기본 컬럼들 (리스트 API에서 제공하는 데이터만)
|
||||
totalWidth += 40; // 체크박스
|
||||
totalWidth += 50; // 번호
|
||||
totalWidth += 120; // 제조사
|
||||
totalWidth += 120; // 장비명
|
||||
totalWidth += 100; // 카테고리
|
||||
totalWidth += 120; // 장비번호
|
||||
totalWidth += 120; // 모델명
|
||||
totalWidth += 50; // 수량
|
||||
totalWidth += 70; // 상태
|
||||
totalWidth += 80; // 입출고일
|
||||
totalWidth += 120; // 입고지
|
||||
totalWidth += 120; // 구매처
|
||||
totalWidth += 100; // 구매일
|
||||
totalWidth += 100; // 구매가격
|
||||
totalWidth += 90; // 관리
|
||||
|
||||
// 상세 컬럼들 (조건부)
|
||||
if (_showDetailedColumns) {
|
||||
totalWidth += 120; // 시리얼번호
|
||||
totalWidth += 120; // 바코드
|
||||
totalWidth += 120; // 현재 위치
|
||||
totalWidth += 100; // 창고 위치 (중복 - 입고지와 다름)
|
||||
totalWidth += 100; // 점검일
|
||||
}
|
||||
|
||||
// padding 추가 (좌우 각 16px)
|
||||
@@ -838,14 +831,13 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
_buildHeaderCell('번호', flex: 1, useExpanded: useExpanded, minWidth: 50),
|
||||
// 제조사
|
||||
_buildHeaderCell('제조사', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 장비명
|
||||
_buildHeaderCell('장비명', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 카테고리
|
||||
_buildHeaderCell('카테고리', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
// 장비번호
|
||||
_buildHeaderCell('장비번호', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 모델명
|
||||
_buildHeaderCell('모델명', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
_buildHeaderCell('시리얼번호', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
_buildHeaderCell('바코드', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
],
|
||||
// 수량
|
||||
_buildHeaderCell('수량', flex: 1, useExpanded: useExpanded, minWidth: 50),
|
||||
@@ -853,20 +845,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
_buildHeaderCell('상태', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
// 입출고일
|
||||
_buildHeaderCell('입출고일', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
// 입고지
|
||||
_buildHeaderCell('입고지', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 구매처
|
||||
_buildHeaderCell('구매처', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 구매일
|
||||
_buildHeaderCell('구매일', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
// 구매가격
|
||||
_buildHeaderCell('구매가격', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
_buildHeaderCell('현재 위치', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
_buildHeaderCell('창고 위치', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
_buildHeaderCell('점검일', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
],
|
||||
// 관리
|
||||
_buildHeaderCell('관리', flex: 2, useExpanded: useExpanded, minWidth: 90),
|
||||
],
|
||||
@@ -924,22 +902,25 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 장비명
|
||||
// 장비번호
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.name,
|
||||
equipment.equipment.name,
|
||||
equipment.equipment.equipmentNumber, // name → equipmentNumber (메인 필드)
|
||||
equipment.equipment.equipmentNumber,
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 카테고리
|
||||
// 모델명
|
||||
_buildDataCell(
|
||||
_buildCategoryWithTooltip(equipment),
|
||||
flex: 2,
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.modelName ?? '-', // 모델명 표시
|
||||
equipment.equipment.modelName ?? '-',
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
@@ -952,15 +933,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.barcode ?? '-',
|
||||
equipment.equipment.barcode ?? '-',
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
],
|
||||
// 수량
|
||||
_buildDataCell(
|
||||
@@ -986,80 +958,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 80,
|
||||
),
|
||||
// 입고지
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.warehouseLocation ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 구매처 (회사명)
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.currentCompany ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 구매일
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.purchaseDate != null
|
||||
? '${equipment.equipment.purchaseDate!.year}/${equipment.equipment.purchaseDate!.month.toString().padLeft(2, '0')}/${equipment.equipment.purchaseDate!.day.toString().padLeft(2, '0')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 구매가격
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.purchasePrice != null
|
||||
? '₩${equipment.equipment.purchasePrice!.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
// 현재 위치 (회사 + 지점)
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
_buildCurrentLocationText(equipment),
|
||||
_buildCurrentLocationText(equipment),
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 창고 위치
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.warehouseLocation ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 점검일 (최근/다음)
|
||||
_buildDataCell(
|
||||
_buildInspectionDateWidget(equipment),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
],
|
||||
// 관리
|
||||
_buildDataCell(
|
||||
_buildActionButtons(equipment.equipment.id ?? 0),
|
||||
@@ -1250,7 +1148,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
final result = await EquipmentHistoryDialog.show(
|
||||
context: context,
|
||||
equipmentId: equipmentId,
|
||||
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.name}',
|
||||
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}', // name → equipmentNumber
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
@@ -1306,81 +1204,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return _getFilteredEquipments();
|
||||
}
|
||||
|
||||
/// 카테고리 축약 표기 함수
|
||||
String _shortenCategory(String category) {
|
||||
if (category.length <= 2) return category;
|
||||
return '${category.substring(0, 2)}...';
|
||||
}
|
||||
|
||||
/// 영어 카테고리를 한국어로 변환
|
||||
String _translateCategory(String category) {
|
||||
const Map<String, String> categoryMap = {
|
||||
// 대분류
|
||||
'Network': '네트워크',
|
||||
'Server': '서버',
|
||||
'Storage': '스토리지',
|
||||
'Security': '보안',
|
||||
'Computer': '컴퓨터',
|
||||
'Mobile': '모바일',
|
||||
'Printer': '프린터',
|
||||
'Monitor': '모니터',
|
||||
'Peripheral': '주변기기',
|
||||
// 중분류
|
||||
'Router': '라우터',
|
||||
'Switch': '스위치',
|
||||
'Firewall': '방화벽',
|
||||
'Laptop': '노트북',
|
||||
'Desktop': '데스크톱',
|
||||
'Tablet': '태블릿',
|
||||
'Smartphone': '스마트폰',
|
||||
'Scanner': '스캐너',
|
||||
'Keyboard': '키보드',
|
||||
'Mouse': '마우스',
|
||||
// 소분류 예시
|
||||
'Wireless': '무선',
|
||||
'Wired': '유선',
|
||||
'Gaming': '게이밍',
|
||||
'Office': '사무용',
|
||||
};
|
||||
|
||||
return categoryMap[category] ?? category;
|
||||
}
|
||||
|
||||
/// 카테고리 툴팁 위젯 (한국어 변환 적용)
|
||||
Widget _buildCategoryWithTooltip(UnifiedEquipment equipment) {
|
||||
// 영어→한국어 변환 적용
|
||||
final translatedCategory = _translateCategory(equipment.equipment.category);
|
||||
final translatedSubCategory = _translateCategory(equipment.equipment.subCategory);
|
||||
final translatedSubSubCategory = _translateCategory(equipment.equipment.subSubCategory);
|
||||
|
||||
final fullCategory = EquipmentDisplayHelper.formatCategory(
|
||||
translatedCategory,
|
||||
translatedSubCategory,
|
||||
translatedSubSubCategory,
|
||||
);
|
||||
|
||||
// 축약 표기 적용 - 비어있지 않은 카테고리만 표시
|
||||
final List<String> parts = [];
|
||||
if (translatedCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedCategory));
|
||||
}
|
||||
if (translatedSubCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedSubCategory));
|
||||
}
|
||||
if (translatedSubSubCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedSubSubCategory));
|
||||
}
|
||||
final shortCategory = parts.join(' > ');
|
||||
|
||||
return Tooltip(
|
||||
message: fullCategory,
|
||||
child: Text(
|
||||
shortCategory,
|
||||
style: ShadcnTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}
|
||||
// 사용하지 않는 카테고리 관련 함수들 제거됨 (리스트 API에서 제공하지 않음)
|
||||
|
||||
/// 캐시된 데이터를 사용한 상태 드롭다운 아이템 생성
|
||||
List<DropdownMenuItem<String>> _buildStatusDropdownItems() {
|
||||
@@ -1416,49 +1240,5 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return items;
|
||||
}
|
||||
|
||||
/// 현재 위치 텍스트 생성 (회사명 + 지점명)
|
||||
String _buildCurrentLocationText(UnifiedEquipment equipment) {
|
||||
final currentCompany = equipment.currentCompany ?? '-';
|
||||
final currentBranch = equipment.currentBranch ?? '';
|
||||
|
||||
if (currentBranch.isNotEmpty) {
|
||||
return '$currentCompany ($currentBranch)';
|
||||
} else {
|
||||
return currentCompany;
|
||||
}
|
||||
}
|
||||
|
||||
/// 점검일 위젯 생성 (최근 점검일/다음 점검일)
|
||||
Widget _buildInspectionDateWidget(UnifiedEquipment equipment) {
|
||||
final lastInspection = equipment.lastInspectionDate;
|
||||
final nextInspection = equipment.nextInspectionDate;
|
||||
|
||||
String displayText = '-';
|
||||
Color? textColor;
|
||||
|
||||
if (nextInspection != null) {
|
||||
final now = DateTime.now();
|
||||
final difference = nextInspection.difference(now).inDays;
|
||||
|
||||
if (difference < 0) {
|
||||
displayText = '점검 필요';
|
||||
textColor = Colors.red;
|
||||
} else if (difference <= 30) {
|
||||
displayText = '${difference}일 후';
|
||||
textColor = Colors.orange;
|
||||
} else {
|
||||
displayText = '${nextInspection.month}/${nextInspection.day}';
|
||||
textColor = Colors.green;
|
||||
}
|
||||
} else if (lastInspection != null) {
|
||||
displayText = '${lastInspection.month}/${lastInspection.day}';
|
||||
}
|
||||
|
||||
return Text(
|
||||
displayText,
|
||||
style: ShadcnTheme.bodySmall.copyWith(
|
||||
color: textColor ?? ShadcnTheme.bodySmall.color,
|
||||
),
|
||||
);
|
||||
}
|
||||
// 사용하지 않는 현재위치, 점검일 관련 함수들 제거됨 (리스트 API에서 제공하지 않음)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user