refactor: Equipment 리스트 화면 API 호환성 개선
- 리스트 API가 제공하지 않는 9개 컬럼 제거 (카테고리, 바코드, 입고지, 구매처, 구매일, 구매가격, 현재위치, 창고위치, 점검일) - 실제 제공되는 데이터만 표시하도록 최적화 (제조사, 장비번호, 모델명, 시리얼번호, 수량, 상태, 입출고일) - Equipment 필드명 변경 대응 (name → equipmentNumber, category 하드코딩 개선) - 불필요한 헬퍼 함수 제거 및 테이블 너비 계산 최적화 - 헬스체크 주기 조정 (30초 → 300초)
This commit is contained in:
@@ -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}$',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user