refactor: Equipment 리스트 화면 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

- 리스트 API가 제공하지 않는 9개 컬럼 제거 (카테고리, 바코드, 입고지, 구매처, 구매일, 구매가격, 현재위치, 창고위치, 점검일)
- 실제 제공되는 데이터만 표시하도록 최적화 (제조사, 장비번호, 모델명, 시리얼번호, 수량, 상태, 입출고일)
- Equipment 필드명 변경 대응 (name → equipmentNumber, category 하드코딩 개선)
- 불필요한 헬퍼 함수 제거 및 테이블 너비 계산 최적화
- 헬스체크 주기 조정 (30초 → 300초)
This commit is contained in:
JiWoong Sul
2025-08-21 20:07:30 +09:00
parent 49089b7814
commit a740ff10c8
3 changed files with 66 additions and 278 deletions

View File

@@ -4,52 +4,52 @@ class AppConstants {
static const int defaultPageSize = 20; static const int defaultPageSize = 20;
static const int maxPageSize = 100; static const int maxPageSize = 100;
static const Duration cacheTimeout = Duration(minutes: 5); static const Duration cacheTimeout = Duration(minutes: 5);
// API 타임아웃 // API 타임아웃
static const Duration apiConnectTimeout = Duration(seconds: 60); static const Duration apiConnectTimeout = Duration(seconds: 60);
static const Duration apiReceiveTimeout = Duration(seconds: 60); static const Duration apiReceiveTimeout = Duration(seconds: 60);
static const Duration healthCheckTimeout = Duration(seconds: 10); static const Duration healthCheckTimeout = Duration(seconds: 10);
static const Duration loginTimeout = Duration(seconds: 10); static const Duration loginTimeout = Duration(seconds: 10);
// 디바운스 시간 // 디바운스 시간
static const Duration searchDebounce = Duration(milliseconds: 500); static const Duration searchDebounce = Duration(milliseconds: 500);
static const Duration licenseSearchDebounce = Duration(milliseconds: 300); static const Duration licenseSearchDebounce = Duration(milliseconds: 300);
// 애니메이션 시간 // 애니메이션 시간
static const Duration autocompleteAnimation = Duration(milliseconds: 200); static const Duration autocompleteAnimation = Duration(milliseconds: 200);
static const Duration formAnimation = Duration(milliseconds: 300); static const Duration formAnimation = Duration(milliseconds: 300);
static const Duration loginAnimation = Duration(milliseconds: 1000); static const Duration loginAnimation = Duration(milliseconds: 1000);
static const Duration loginSubAnimation = Duration(milliseconds: 800); static const Duration loginSubAnimation = Duration(milliseconds: 800);
// 라이선스 만료 기간 // 라이선스 만료 기간
static const int licenseExpiryWarningDays = 30; static const int licenseExpiryWarningDays = 30;
static const int licenseExpiryCautionDays = 60; static const int licenseExpiryCautionDays = 60;
static const int licenseExpiryInfoDays = 90; 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'; static const String accessTokenKey = 'access_token';
static const String refreshTokenKey = 'refresh_token'; static const String refreshTokenKey = 'refresh_token';
static const String tokenTypeKey = 'token_type'; static const String tokenTypeKey = 'token_type';
static const String expiresInKey = 'expires_in'; static const String expiresInKey = 'expires_in';
// 사용자 권한 매핑 // 사용자 권한 매핑
static const Map<String, String> flutterToBackendRole = { static const Map<String, String> flutterToBackendRole = {
'S': 'admin', // Super user 'S': 'admin', // Super user
'M': 'manager', // Manager 'M': 'manager', // Manager
'U': 'staff', // User 'U': 'staff', // User
'V': 'viewer', // Viewer 'V': 'viewer', // Viewer
}; };
static const Map<String, String> backendToFlutterRole = { static const Map<String, String> backendToFlutterRole = {
'admin': 'S', 'admin': 'S',
'manager': 'M', 'manager': 'M',
'staff': 'U', 'staff': 'U',
'viewer': 'V', 'viewer': 'V',
}; };
// 장비 상태 // 장비 상태
static const Map<String, String> equipmentStatus = { static const Map<String, String> equipmentStatus = {
'available': '사용가능', 'available': '사용가능',
@@ -58,7 +58,7 @@ class AppConstants {
'disposed': '폐기', 'disposed': '폐기',
'rented': '대여중', 'rented': '대여중',
}; };
// 정렬 옵션 // 정렬 옵션
static const Map<String, String> sortOptions = { static const Map<String, String> sortOptions = {
'created_at': '생성일', 'created_at': '생성일',
@@ -66,34 +66,39 @@ class AppConstants {
'name': '이름', 'name': '이름',
'status': '상태', 'status': '상태',
}; };
// 날짜 형식 // 날짜 형식
static const String dateFormat = 'yyyy-MM-dd'; static const String dateFormat = 'yyyy-MM-dd';
static const String dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'; static const String dateTimeFormat = 'yyyy-MM-dd HH:mm:ss';
// 파일 업로드 // 파일 업로드
static const int maxFileSize = 10 * 1024 * 1024; // 10MB static const int maxFileSize = 10 * 1024 * 1024; // 10MB
static const List<String> allowedFileExtensions = [ static const List<String> allowedFileExtensions = [
'jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx' 'jpg',
'jpeg',
'png',
'pdf',
'doc',
'docx',
'xls',
'xlsx',
]; ];
// 에러 메시지 // 에러 메시지
static const String networkError = '네트워크 연결을 확인해주세요.'; static const String networkError = '네트워크 연결을 확인해주세요.';
static const String timeoutError = '요청 시간이 초과되었습니다.'; static const String timeoutError = '요청 시간이 초과되었습니다.';
static const String unauthorizedError = '인증이 필요합니다.'; static const String unauthorizedError = '인증이 필요합니다.';
static const String serverError = '서버 오류가 발생했습니다.'; static const String serverError = '서버 오류가 발생했습니다.';
static const String unknownError = '알 수 없는 오류가 발생했습니다.'; static const String unknownError = '알 수 없는 오류가 발생했습니다.';
// 정규식 패턴 // 정규식 패턴
static final RegExp emailRegex = RegExp( static final RegExp emailRegex = RegExp(
r'^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+', r'^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+',
); );
static final RegExp phoneRegex = RegExp( static final RegExp phoneRegex = RegExp(r'^01[0-9]{1}-?[0-9]{4}-?[0-9]{4}$');
r'^01[0-9]{1}-?[0-9]{4}-?[0-9]{4}$',
);
static final RegExp businessNumberRegex = RegExp( static final RegExp businessNumberRegex = RegExp(
r'^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$', r'^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$',
); );
} }

View File

@@ -108,9 +108,11 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
manufacturer: dto.manufacturer ?? 'Unknown', manufacturer: dto.manufacturer ?? 'Unknown',
equipmentNumber: dto.equipmentNumber ?? 'Unknown', // name → equipmentNumber (required) equipmentNumber: dto.equipmentNumber ?? 'Unknown', // name → equipmentNumber (required)
modelName: dto.modelName ?? dto.equipmentNumber ?? 'Unknown', // 새로운 필수 필드 (required) modelName: dto.modelName ?? dto.equipmentNumber ?? 'Unknown', // 새로운 필수 필드 (required)
category1: 'Equipment', // category → category1 (required) // 🔧 [BUG FIX] 하드코딩 제거 - 백엔드 API에서 카테고리 정보 미제공 시 기본값 사용
category2: 'General', // subCategory → category2 (required) // TODO: 백엔드 API에서 category1/2/3 필드 추가 필요
category3: 'Standard', // subSubCategory → category3 (required) category1: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
category2: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
category3: 'N/A', // 백엔드에서 카테고리 정보 미제공 시 기본값
serialNumber: dto.serialNumber, serialNumber: dto.serialNumber,
quantity: 1, // 기본 수량 quantity: 1, // 기본 수량
); );
@@ -154,7 +156,8 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
@override @override
bool filterItem(UnifiedEquipment item, String query) { bool filterItem(UnifiedEquipment item, String query) {
final q = query.toLowerCase(); 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.serialNumber?.toLowerCase().contains(q) ?? false) ||
(item.equipment.manufacturer.toLowerCase().contains(q)) || (item.equipment.manufacturer.toLowerCase().contains(q)) ||
(item.notes?.toLowerCase().contains(q) ?? false) || (item.notes?.toLowerCase().contains(q) ?? false) ||
@@ -311,7 +314,7 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
reason: reason ?? '폐기 처리', reason: reason ?? '폐기 처리',
); );
} catch (e) { } catch (e) {
failedEquipments.add('${equipment.equipment.manufacturer} ${equipment.equipment.name}'); failedEquipments.add('${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}'); // name → equipmentNumber
} }
} }

View File

@@ -195,10 +195,11 @@ class _EquipmentListState extends State<EquipmentList> {
final keyword = _appliedSearchKeyword.toLowerCase(); final keyword = _appliedSearchKeyword.toLowerCase();
return [ return [
e.equipment.manufacturer, e.equipment.manufacturer,
e.equipment.name, e.equipment.equipmentNumber, // name → equipmentNumber (메인 필드)
e.equipment.category, e.equipment.modelName ?? '', // 모델명 추가
e.equipment.subCategory, e.equipment.category1, // category → category1 (메인 필드)
e.equipment.subSubCategory, e.equipment.category2, // subCategory → category2 (메인 필드)
e.equipment.category3, // subSubCategory → category3 (메인 필드)
e.equipment.serialNumber ?? '', e.equipment.serialNumber ?? '',
e.equipment.barcode ?? '', e.equipment.barcode ?? '',
e.equipment.remark ?? '', e.equipment.remark ?? '',
@@ -288,7 +289,7 @@ class _EquipmentListState extends State<EquipmentList> {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
child: Text( child: Text(
'${equipment.manufacturer} ${equipment.name}', '${equipment.manufacturer} ${equipment.equipmentNumber}', // name → equipmentNumber
style: const TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
), ),
); );
@@ -434,7 +435,7 @@ class _EquipmentListState extends State<EquipmentList> {
final result = await EquipmentHistoryDialog.show( final result = await EquipmentHistoryDialog.show(
context: context, context: context,
equipmentId: equipment.equipment.id!, equipmentId: equipment.equipment.id!,
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.name}', equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}', // name → equipmentNumber
); );
if (result == true) { if (result == true) {
@@ -731,28 +732,20 @@ class _EquipmentListState extends State<EquipmentList> {
double _getMinimumTableWidth(List<UnifiedEquipment> pagedEquipments) { double _getMinimumTableWidth(List<UnifiedEquipment> pagedEquipments) {
double totalWidth = 0; double totalWidth = 0;
// 기본 컬럼들 (최소 너비) // 기본 컬럼들 (리스트 API에서 제공하는 데이터만)
totalWidth += 40; // 체크박스 totalWidth += 40; // 체크박스
totalWidth += 50; // 번호 totalWidth += 50; // 번호
totalWidth += 120; // 제조사 totalWidth += 120; // 제조사
totalWidth += 120; // 장비 totalWidth += 120; // 장비번호
totalWidth += 100; // 카테고리 totalWidth += 120; // 모델명
totalWidth += 50; // 수량 totalWidth += 50; // 수량
totalWidth += 70; // 상태 totalWidth += 70; // 상태
totalWidth += 80; // 입출고일 totalWidth += 80; // 입출고일
totalWidth += 120; // 입고지
totalWidth += 120; // 구매처
totalWidth += 100; // 구매일
totalWidth += 100; // 구매가격
totalWidth += 90; // 관리 totalWidth += 90; // 관리
// 상세 컬럼들 (조건부) // 상세 컬럼들 (조건부)
if (_showDetailedColumns) { if (_showDetailedColumns) {
totalWidth += 120; // 시리얼번호 totalWidth += 120; // 시리얼번호
totalWidth += 120; // 바코드
totalWidth += 120; // 현재 위치
totalWidth += 100; // 창고 위치 (중복 - 입고지와 다름)
totalWidth += 100; // 점검일
} }
// padding 추가 (좌우 각 16px) // padding 추가 (좌우 각 16px)
@@ -838,14 +831,13 @@ class _EquipmentListState extends State<EquipmentList> {
_buildHeaderCell('번호', flex: 1, useExpanded: useExpanded, minWidth: 50), _buildHeaderCell('번호', flex: 1, useExpanded: useExpanded, minWidth: 50),
// 제조사 // 제조사
_buildHeaderCell('제조사', flex: 3, useExpanded: useExpanded, minWidth: 120), _buildHeaderCell('제조사', flex: 3, useExpanded: useExpanded, minWidth: 120),
// 장비 // 장비번호
_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),
// 상세 정보 (조건부) // 상세 정보 (조건부)
if (_showDetailedColumns) ...[ if (_showDetailedColumns) ...[
_buildHeaderCell('시리얼번호', flex: 3, useExpanded: useExpanded, minWidth: 120), _buildHeaderCell('시리얼번호', flex: 3, useExpanded: useExpanded, minWidth: 120),
_buildHeaderCell('바코드', flex: 3, useExpanded: useExpanded, minWidth: 120),
], ],
// 수량 // 수량
_buildHeaderCell('수량', flex: 1, useExpanded: useExpanded, minWidth: 50), _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: 70),
// 입출고일 // 입출고일
_buildHeaderCell('입출고일', flex: 2, useExpanded: useExpanded, minWidth: 80), _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), _buildHeaderCell('관리', flex: 2, useExpanded: useExpanded, minWidth: 90),
], ],
@@ -924,22 +902,25 @@ class _EquipmentListState extends State<EquipmentList> {
useExpanded: useExpanded, useExpanded: useExpanded,
minWidth: 120, minWidth: 120,
), ),
// 장비 // 장비번호
_buildDataCell( _buildDataCell(
_buildTextWithTooltip( _buildTextWithTooltip(
equipment.equipment.name, equipment.equipment.equipmentNumber, // name → equipmentNumber (메인 필드)
equipment.equipment.name, equipment.equipment.equipmentNumber,
), ),
flex: 3, flex: 3,
useExpanded: useExpanded, useExpanded: useExpanded,
minWidth: 120, minWidth: 120,
), ),
// 카테고리 // 모델명
_buildDataCell( _buildDataCell(
_buildCategoryWithTooltip(equipment), _buildTextWithTooltip(
flex: 2, equipment.equipment.modelName ?? '-', // 모델명 표시
equipment.equipment.modelName ?? '-',
),
flex: 3,
useExpanded: useExpanded, useExpanded: useExpanded,
minWidth: 100, minWidth: 120,
), ),
// 상세 정보 (조건부) // 상세 정보 (조건부)
if (_showDetailedColumns) ...[ if (_showDetailedColumns) ...[
@@ -952,15 +933,6 @@ class _EquipmentListState extends State<EquipmentList> {
useExpanded: useExpanded, useExpanded: useExpanded,
minWidth: 120, minWidth: 120,
), ),
_buildDataCell(
_buildTextWithTooltip(
equipment.equipment.barcode ?? '-',
equipment.equipment.barcode ?? '-',
),
flex: 3,
useExpanded: useExpanded,
minWidth: 120,
),
], ],
// 수량 // 수량
_buildDataCell( _buildDataCell(
@@ -986,80 +958,6 @@ class _EquipmentListState extends State<EquipmentList> {
useExpanded: useExpanded, useExpanded: useExpanded,
minWidth: 80, 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( _buildDataCell(
_buildActionButtons(equipment.equipment.id ?? 0), _buildActionButtons(equipment.equipment.id ?? 0),
@@ -1250,7 +1148,7 @@ class _EquipmentListState extends State<EquipmentList> {
final result = await EquipmentHistoryDialog.show( final result = await EquipmentHistoryDialog.show(
context: context, context: context,
equipmentId: equipmentId, equipmentId: equipmentId,
equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.name}', equipmentName: '${equipment.equipment.manufacturer} ${equipment.equipment.equipmentNumber}', // name → equipmentNumber
); );
if (result == true) { if (result == true) {
@@ -1306,81 +1204,7 @@ class _EquipmentListState extends State<EquipmentList> {
return _getFilteredEquipments(); return _getFilteredEquipments();
} }
/// 카테고리 축약 표기 함수 // 사용하지 않는 카테고리 관련 함수들 제거됨 (리스트 API에서 제공하지 않음)
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,
),
);
}
/// 캐시된 데이터를 사용한 상태 드롭다운 아이템 생성 /// 캐시된 데이터를 사용한 상태 드롭다운 아이템 생성
List<DropdownMenuItem<String>> _buildStatusDropdownItems() { List<DropdownMenuItem<String>> _buildStatusDropdownItems() {
@@ -1416,49 +1240,5 @@ class _EquipmentListState extends State<EquipmentList> {
return items; return items;
} }
/// 현재 위치 텍스트 생성 (회사명 + 지점명) // 사용하지 않는 현재위치, 점검일 관련 함수들 제거됨 (리스트 API에서 제공하지 않음)
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,
),
);
}
} }