backup: 사용하지 않는 파일 삭제 전 복구 지점
- 전체 371개 파일 중 82개 미사용 파일 식별 - Phase 1: 33개 파일 삭제 예정 (100% 안전) - Phase 2: 30개 파일 삭제 검토 예정 - Phase 3: 19개 파일 수동 검토 예정 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,10 @@ import 'package:superport/screens/common/widgets/standard_states.dart';
|
||||
import 'package:superport/screens/common/layouts/base_list_screen.dart';
|
||||
import 'package:superport/screens/equipment/controllers/equipment_list_controller.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/core/constants/app_constants.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/screens/equipment/widgets/equipment_history_dialog.dart';
|
||||
import 'package:superport/screens/equipment/widgets/equipment_search_dialog.dart';
|
||||
|
||||
/// shadcn/ui 스타일로 재설계된 장비 관리 화면
|
||||
class EquipmentList extends StatefulWidget {
|
||||
@@ -38,7 +40,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = EquipmentListController();
|
||||
_controller.pageSize = 10; // 페이지 크기 설정
|
||||
_controller.pageSize = AppConstants.equipmentPageSize; // 페이지 크기 설정
|
||||
_setInitialFilter();
|
||||
_preloadDropdownData(); // 드롭다운 데이터 미리 로드
|
||||
|
||||
@@ -76,11 +78,12 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
_adjustColumnsForScreenSize();
|
||||
}
|
||||
|
||||
/// 화면 크기에 따라 컬럼 표시 조정
|
||||
/// 화면 크기에 따라 컬럼 표시 조정 - 다단계 반응형
|
||||
void _adjustColumnsForScreenSize() {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
setState(() {
|
||||
_showDetailedColumns = width > 900;
|
||||
// 1200px 이상에서만 상세 컬럼 (바코드, 구매가격, 구매일, 보증기간) 표시
|
||||
_showDetailedColumns = width > 1200;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,6 +148,14 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
_controller.changeStatusFilter(_controller.selectedStatusFilter);
|
||||
}
|
||||
|
||||
/// 회사 필터 변경
|
||||
Future<void> _onCompanyFilterChanged(int? companyId) async {
|
||||
setState(() {
|
||||
_controller.filterByCompany(companyId);
|
||||
_controller.goToPage(1);
|
||||
});
|
||||
}
|
||||
|
||||
/// 검색 실행
|
||||
void _onSearch() async {
|
||||
setState(() {
|
||||
@@ -185,10 +196,10 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
equipments = equipments.where((e) {
|
||||
final keyword = _appliedSearchKeyword.toLowerCase();
|
||||
return [
|
||||
e.equipment.model?.vendor?.name ?? '', // Vendor 이름
|
||||
e.equipment.serialNumber ?? '', // 시리얼 번호 (메인 필드)
|
||||
e.equipment.model?.name ?? '', // Model 이름
|
||||
e.equipment.serialNumber ?? '', // 시리얼 번호 (중복 제거)
|
||||
e.vendorName ?? '', // 백엔드 직접 제공 Vendor 이름
|
||||
e.modelName ?? '', // 백엔드 직접 제공 Model 이름
|
||||
e.companyName ?? '', // 백엔드 직접 제공 Company 이름
|
||||
e.equipment.serialNumber ?? '', // 시리얼 번호
|
||||
e.equipment.barcode ?? '', // 바코드
|
||||
e.equipment.remark ?? '', // 비고
|
||||
].any((field) => field.toLowerCase().contains(keyword.toLowerCase()));
|
||||
@@ -285,7 +296,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
'${equipment.model?.vendor?.name ?? 'N/A'} ${equipment.serialNumber}', // Vendor + Equipment Number
|
||||
'${unifiedEquipment.vendorName ?? 'N/A'} ${equipment.serialNumber}', // 백엔드 직접 제공 Vendor + Equipment Number
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
@@ -523,6 +534,9 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
final filteredEquipments = _getFilteredEquipments();
|
||||
// 백엔드 API에서 제공하는 실제 전체 아이템 수 사용
|
||||
final totalCount = controller.total;
|
||||
|
||||
// 디버그: 페이지네이션 상태 확인
|
||||
print('DEBUG Pagination: total=${controller.total}, totalPages=${controller.totalPages}, pageSize=${controller.pageSize}, currentPage=${controller.currentPage}');
|
||||
|
||||
return BaseListScreen(
|
||||
isLoading: controller.isLoading && controller.equipments.isEmpty,
|
||||
@@ -543,8 +557,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
// 데이터 테이블
|
||||
dataTable: _buildDataTable(filteredEquipments),
|
||||
|
||||
// 페이지네이션
|
||||
pagination: controller.totalPages > 1 ? Pagination(
|
||||
// 페이지네이션 - 조건 수정으로 표시 개선
|
||||
pagination: controller.total > controller.pageSize ? Pagination(
|
||||
totalCount: controller.total,
|
||||
currentPage: controller.currentPage,
|
||||
pageSize: controller.pageSize,
|
||||
@@ -621,6 +635,25 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 16),
|
||||
|
||||
// 회사별 필터 드롭다운
|
||||
SizedBox(
|
||||
height: 40,
|
||||
width: 150,
|
||||
child: ShadSelect<int?>(
|
||||
selectedOptionBuilder: (context, value) => Text(
|
||||
value == null ? '전체 회사' : _getCompanyDisplayText(value),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
placeholder: const Text('회사 선택'),
|
||||
options: _buildCompanySelectOptions(),
|
||||
onChanged: (value) {
|
||||
_onCompanyFilterChanged(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -631,6 +664,19 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
leftActions: [
|
||||
// 라우트별 액션 버튼
|
||||
_buildRouteSpecificActions(selectedInCount, selectedOutCount, selectedRentCount),
|
||||
const SizedBox(width: 8),
|
||||
// 검색 버튼 추가
|
||||
ShadButton.outline(
|
||||
onPressed: () => _showEquipmentSearchDialog(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.search, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
const Text('고급 검색'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
rightActions: [
|
||||
// 관리자용 비활성 포함 체크박스
|
||||
@@ -667,7 +713,9 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
Widget _buildRouteSpecificActions(int selectedInCount, int selectedOutCount, int selectedRentCount) {
|
||||
switch (widget.currentRoute) {
|
||||
case Routes.equipmentInList:
|
||||
return Row(
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
ShadcnButton(
|
||||
text: '출고',
|
||||
@@ -675,7 +723,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
variant: selectedInCount > 0 ? ShadcnButtonVariant.primary : ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.exit_to_app, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '입고',
|
||||
onPressed: () async {
|
||||
@@ -695,7 +742,9 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
],
|
||||
);
|
||||
case Routes.equipmentOutList:
|
||||
return Row(
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
ShadcnButton(
|
||||
text: '재입고',
|
||||
@@ -710,9 +759,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
variant: selectedOutCount > 0 ? ShadcnButtonVariant.primary : ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.assignment_return, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '수리 요청',
|
||||
text: '수리',
|
||||
onPressed: selectedOutCount > 0
|
||||
? () => ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
@@ -727,7 +775,9 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
],
|
||||
);
|
||||
case Routes.equipmentRentList:
|
||||
return Row(
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
ShadcnButton(
|
||||
text: '반납',
|
||||
@@ -742,7 +792,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
variant: selectedRentCount > 0 ? ShadcnButtonVariant.primary : ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.keyboard_return, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '연장',
|
||||
onPressed: selectedRentCount > 0
|
||||
@@ -759,7 +808,9 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
],
|
||||
);
|
||||
default:
|
||||
return Row(
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
ShadcnButton(
|
||||
text: '입고',
|
||||
@@ -777,24 +828,21 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
textColor: Colors.white,
|
||||
icon: const Icon(Icons.add, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '출고 처리',
|
||||
text: '출고',
|
||||
onPressed: selectedInCount > 0 ? _handleOutEquipment : null,
|
||||
variant: selectedInCount > 0 ? ShadcnButtonVariant.primary : ShadcnButtonVariant.secondary,
|
||||
textColor: selectedInCount > 0 ? Colors.white : null,
|
||||
icon: const Icon(Icons.local_shipping, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '대여 처리',
|
||||
text: '대여',
|
||||
onPressed: selectedInCount > 0 ? _handleRentEquipment : null,
|
||||
variant: selectedInCount > 0 ? ShadcnButtonVariant.secondary : ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.assignment, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadcnButton(
|
||||
text: '폐기 처리',
|
||||
text: '폐기',
|
||||
onPressed: selectedInCount > 0 ? _handleDisposeEquipment : null,
|
||||
variant: selectedInCount > 0 ? ShadcnButtonVariant.destructive : ShadcnButtonVariant.secondary,
|
||||
icon: const Icon(Icons.delete, size: 16),
|
||||
@@ -805,28 +853,36 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
}
|
||||
|
||||
|
||||
/// 최소 테이블 너비 계산
|
||||
double _getMinimumTableWidth(List<UnifiedEquipment> pagedEquipments) {
|
||||
/// 최소 테이블 너비 계산 - 반응형 최적화
|
||||
double _getMinimumTableWidth(List<UnifiedEquipment> pagedEquipments, double availableWidth) {
|
||||
double totalWidth = 0;
|
||||
|
||||
// 기본 컬럼들 (리스트 API에서 제공하는 데이터만)
|
||||
totalWidth += 40; // 체크박스
|
||||
totalWidth += 50; // 번호
|
||||
totalWidth += 120; // 제조사
|
||||
totalWidth += 120; // 장비번호
|
||||
totalWidth += 120; // 모델명
|
||||
totalWidth += 50; // 수량
|
||||
totalWidth += 70; // 상태
|
||||
totalWidth += 80; // 입출고일
|
||||
totalWidth += 90; // 관리
|
||||
// 필수 컬럼들 (항상 표시) - 더 작게 조정
|
||||
totalWidth += 30; // 체크박스 (35->30)
|
||||
totalWidth += 35; // 번호 (40->35)
|
||||
totalWidth += 70; // 회사명 (90->70)
|
||||
totalWidth += 60; // 제조사 (80->60)
|
||||
totalWidth += 80; // 모델명 (100->80)
|
||||
totalWidth += 70; // 장비번호 (90->70)
|
||||
totalWidth += 50; // 상태 (60->50)
|
||||
totalWidth += 90; // 관리 (120->90, 아이콘 전용으로 최적화)
|
||||
|
||||
// 상세 컬럼들 (조건부)
|
||||
if (_showDetailedColumns) {
|
||||
totalWidth += 120; // 시리얼번호
|
||||
// 중간 화면용 추가 컬럼들 (800px 이상)
|
||||
if (availableWidth > 800) {
|
||||
totalWidth += 35; // 수량 (40->35)
|
||||
totalWidth += 70; // 입출고일 (80->70)
|
||||
}
|
||||
|
||||
// padding 추가 (좌우 각 16px)
|
||||
totalWidth += 32;
|
||||
// 상세 컬럼들 (1200px 이상에서만 표시)
|
||||
if (_showDetailedColumns && availableWidth > 1200) {
|
||||
totalWidth += 70; // 바코드 (90->70)
|
||||
totalWidth += 70; // 구매가격 (80->70)
|
||||
totalWidth += 70; // 구매일 (80->70)
|
||||
totalWidth += 80; // 보증기간 (90->80)
|
||||
}
|
||||
|
||||
// padding 추가 (좌우 각 2px로 축소)
|
||||
totalWidth += 4;
|
||||
|
||||
return totalWidth;
|
||||
}
|
||||
@@ -873,16 +929,16 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
}
|
||||
|
||||
/// 유연한 테이블 빌더 - Virtual Scrolling 적용
|
||||
Widget _buildFlexibleTable(List<UnifiedEquipment> pagedEquipments, {required bool useExpanded}) {
|
||||
Widget _buildFlexibleTable(List<UnifiedEquipment> pagedEquipments, {required bool useExpanded, required double availableWidth}) {
|
||||
final hasOutOrRent = pagedEquipments.any((e) =>
|
||||
e.status == EquipmentStatus.out || e.status == EquipmentStatus.rent
|
||||
);
|
||||
|
||||
// 헤더를 별도로 빌드
|
||||
// 헤더를 별도로 빌드 - 반응형 컬럼 적용
|
||||
Widget header = Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: ShadcnTheme.spacing4,
|
||||
vertical: 10,
|
||||
horizontal: ShadcnTheme.spacing1, // spacing2 -> spacing1로 더 축소
|
||||
vertical: 6, // 8 -> 6으로 더 축소
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.muted.withValues(alpha: 0.3),
|
||||
@@ -892,6 +948,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 필수 컬럼들 (항상 표시) - 축소된 너비 적용
|
||||
// 체크박스
|
||||
_buildDataCell(
|
||||
ShadCheckbox(
|
||||
@@ -900,30 +957,38 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
flex: 1,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 40,
|
||||
minWidth: 30,
|
||||
),
|
||||
// 번호
|
||||
_buildHeaderCell('번호', flex: 1, useExpanded: useExpanded, minWidth: 50),
|
||||
_buildHeaderCell('번호', flex: 1, useExpanded: useExpanded, minWidth: 35),
|
||||
// 회사명 (소유회사)
|
||||
_buildHeaderCell('소유회사', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
// 제조사
|
||||
_buildHeaderCell('제조사', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 장비번호
|
||||
_buildHeaderCell('장비번호', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
_buildHeaderCell('제조사', flex: 2, useExpanded: useExpanded, minWidth: 60),
|
||||
// 모델명
|
||||
_buildHeaderCell('모델명', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 상세 정보 (조건부) - 바코드로 변경
|
||||
if (_showDetailedColumns) ...[
|
||||
_buildHeaderCell('바코드', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
],
|
||||
// 수량
|
||||
_buildHeaderCell('수량', flex: 1, useExpanded: useExpanded, minWidth: 50),
|
||||
// 재고 상태
|
||||
_buildHeaderCell('재고', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
_buildHeaderCell('모델명', flex: 3, useExpanded: useExpanded, minWidth: 80),
|
||||
// 장비번호
|
||||
_buildHeaderCell('장비번호', flex: 3, useExpanded: useExpanded, minWidth: 70),
|
||||
// 상태
|
||||
_buildHeaderCell('상태', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
// 입출고일
|
||||
_buildHeaderCell('입출고일', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
_buildHeaderCell('상태', flex: 2, useExpanded: useExpanded, minWidth: 50),
|
||||
// 관리
|
||||
_buildHeaderCell('관리', flex: 2, useExpanded: useExpanded, minWidth: 90),
|
||||
|
||||
// 중간 화면용 컬럼들 (800px 이상)
|
||||
if (availableWidth > 800) ...[
|
||||
// 수량
|
||||
_buildHeaderCell('수량', flex: 1, useExpanded: useExpanded, minWidth: 35),
|
||||
// 입출고일
|
||||
_buildHeaderCell('입출고일', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
],
|
||||
|
||||
// 상세 컬럼들 (1200px 이상에서만 표시)
|
||||
if (_showDetailedColumns && availableWidth > 1200) ...[
|
||||
_buildHeaderCell('바코드', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
_buildHeaderCell('구매가격', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
_buildHeaderCell('구매일', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
_buildHeaderCell('보증기간', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -959,8 +1024,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: ShadcnTheme.spacing4,
|
||||
vertical: 4,
|
||||
horizontal: ShadcnTheme.spacing1, // spacing2 -> spacing1로 더 축소
|
||||
vertical: 2, // 3 -> 2로 더 축소
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
@@ -969,6 +1034,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 필수 컬럼들 (항상 표시) - 축소된 너비 적용
|
||||
// 체크박스
|
||||
_buildDataCell(
|
||||
ShadCheckbox(
|
||||
@@ -981,7 +1047,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
flex: 1,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 40,
|
||||
minWidth: 30,
|
||||
),
|
||||
// 번호
|
||||
_buildDataCell(
|
||||
@@ -991,17 +1057,37 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
flex: 1,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 50,
|
||||
minWidth: 35,
|
||||
),
|
||||
// 소유회사
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.companyName ?? 'N/A',
|
||||
equipment.companyName ?? 'N/A',
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 제조사
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.model?.vendor?.name ?? 'N/A',
|
||||
equipment.equipment.model?.vendor?.name ?? 'N/A',
|
||||
equipment.vendorName ?? 'N/A',
|
||||
equipment.vendorName ?? 'N/A',
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 60,
|
||||
),
|
||||
// 모델명
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.modelName ?? '-',
|
||||
equipment.modelName ?? '-',
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
minWidth: 80,
|
||||
),
|
||||
// 장비번호
|
||||
_buildDataCell(
|
||||
@@ -1011,68 +1097,120 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 모델명
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.model?.name ?? '-',
|
||||
equipment.equipment.model?.name ?? '-',
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 상세 정보 (조건부) - 바코드로 변경
|
||||
if (_showDetailedColumns) ...[
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.barcode ?? '-',
|
||||
equipment.equipment.barcode ?? '-',
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
],
|
||||
// 수량 (백엔드에서 관리하지 않으므로 고정값)
|
||||
_buildDataCell(
|
||||
Text(
|
||||
'1',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 1,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 50,
|
||||
),
|
||||
// 재고 상태
|
||||
_buildDataCell(
|
||||
_buildInventoryStatus(equipment),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 80,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 상태
|
||||
_buildDataCell(
|
||||
_buildStatusBadge(equipment.status),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
minWidth: 50,
|
||||
),
|
||||
// 입출고일
|
||||
// 관리 (아이콘 전용 버튼으로 최적화)
|
||||
_buildDataCell(
|
||||
_buildCreatedDateWidget(equipment),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 80,
|
||||
),
|
||||
// 관리
|
||||
_buildDataCell(
|
||||
_buildActionButtons(equipment.equipment.id ?? 0),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Tooltip(
|
||||
message: '이력 보기',
|
||||
child: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _showEquipmentHistoryDialog(equipment.equipment.id ?? 0),
|
||||
child: const Icon(Icons.history, size: 16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Tooltip(
|
||||
message: '수정',
|
||||
child: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _handleEdit(equipment),
|
||||
child: const Icon(Icons.edit, size: 16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Tooltip(
|
||||
message: '삭제',
|
||||
child: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _handleDelete(equipment),
|
||||
child: const Icon(Icons.delete_outline, size: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 90,
|
||||
),
|
||||
|
||||
// 중간 화면용 컬럼들 (800px 이상)
|
||||
if (availableWidth > 800) ...[
|
||||
// 수량 (백엔드에서 관리하지 않으므로 고정값)
|
||||
_buildDataCell(
|
||||
Text(
|
||||
'1',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 1,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 35,
|
||||
),
|
||||
// 입출고일
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
_formatDate(equipment.date),
|
||||
_formatDate(equipment.date),
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
],
|
||||
|
||||
// 상세 컬럼들 (1200px 이상에서만 표시)
|
||||
if (_showDetailedColumns && availableWidth > 1200) ...[
|
||||
// 바코드
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
equipment.equipment.barcode ?? '-',
|
||||
equipment.equipment.barcode ?? '-',
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 구매가격
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
_formatPrice(equipment.equipment.purchasePrice),
|
||||
_formatPrice(equipment.equipment.purchasePrice),
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 구매일
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
_formatDate(equipment.equipment.purchaseDate),
|
||||
_formatDate(equipment.equipment.purchaseDate),
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 보증기간
|
||||
_buildDataCell(
|
||||
_buildTextWithTooltip(
|
||||
_formatWarrantyPeriod(equipment.equipment.warrantyStartDate, equipment.equipment.warrantyEndDate),
|
||||
_formatWarrantyPeriod(equipment.equipment.warrantyStartDate, equipment.equipment.warrantyEndDate),
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 80,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -1127,7 +1265,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final availableWidth = constraints.maxWidth;
|
||||
final minimumWidth = _getMinimumTableWidth(pagedEquipments);
|
||||
final minimumWidth = _getMinimumTableWidth(pagedEquipments, availableWidth);
|
||||
final needsHorizontalScroll = minimumWidth > availableWidth;
|
||||
|
||||
if (needsHorizontalScroll) {
|
||||
@@ -1137,12 +1275,12 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
controller: _horizontalScrollController,
|
||||
child: SizedBox(
|
||||
width: minimumWidth,
|
||||
child: _buildFlexibleTable(pagedEquipments, useExpanded: false),
|
||||
child: _buildFlexibleTable(pagedEquipments, useExpanded: false, availableWidth: availableWidth),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 충분한 공간이 있을 때는 Expanded 사용
|
||||
return _buildFlexibleTable(pagedEquipments, useExpanded: true);
|
||||
return _buildFlexibleTable(pagedEquipments, useExpanded: true, availableWidth: availableWidth);
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -1161,6 +1299,35 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 가격 포맷팅
|
||||
String _formatPrice(double? price) {
|
||||
if (price == null) return '-';
|
||||
return '${(price / 10000).toStringAsFixed(0)}만원';
|
||||
}
|
||||
|
||||
/// 날짜 포맷팅
|
||||
String _formatDate(DateTime? date) {
|
||||
if (date == null) return '-';
|
||||
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
/// 보증기간 포맷팅
|
||||
String _formatWarrantyPeriod(DateTime? startDate, DateTime? endDate) {
|
||||
if (startDate == null || endDate == null) return '-';
|
||||
|
||||
final now = DateTime.now();
|
||||
final isExpired = now.isAfter(endDate);
|
||||
final remainingDays = isExpired ? 0 : endDate.difference(now).inDays;
|
||||
|
||||
if (isExpired) {
|
||||
return '만료됨';
|
||||
} else if (remainingDays <= 30) {
|
||||
return '$remainingDays일 남음';
|
||||
} else {
|
||||
return _formatDate(endDate);
|
||||
}
|
||||
}
|
||||
|
||||
/// 재고 상태 위젯 빌더 (백엔드 기반 단순화)
|
||||
Widget _buildInventoryStatus(UnifiedEquipment equipment) {
|
||||
// 백엔드 Equipment_History 기반으로 단순 상태만 표시
|
||||
@@ -1260,41 +1427,32 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: IconButton(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 30,
|
||||
minHeight: 30,
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
icon: const Icon(Icons.history, size: 16),
|
||||
onPressed: () => _showEquipmentHistoryDialog(equipmentId),
|
||||
tooltip: '이력',
|
||||
// 이력 버튼 - 텍스트 + 아이콘으로 강화
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _showEquipmentHistoryDialog(equipmentId),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Icon(Icons.history, size: 14),
|
||||
SizedBox(width: 4),
|
||||
Text('이력', style: TextStyle(fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: IconButton(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 30,
|
||||
minHeight: 30,
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
icon: const Icon(Icons.edit_outlined, size: 16),
|
||||
onPressed: () => _handleEditById(equipmentId),
|
||||
tooltip: '편집',
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
// 편집 버튼
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _handleEditById(equipmentId),
|
||||
child: const Icon(Icons.edit_outlined, size: 14),
|
||||
),
|
||||
Flexible(
|
||||
child: IconButton(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 30,
|
||||
minHeight: 30,
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
icon: const Icon(Icons.delete_outline, size: 16),
|
||||
onPressed: () => _handleDeleteById(equipmentId),
|
||||
tooltip: '삭제',
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
// 삭제 버튼
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _handleDeleteById(equipmentId),
|
||||
child: const Icon(Icons.delete_outline, size: 14),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -1312,7 +1470,7 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
final result = await EquipmentHistoryDialog.show(
|
||||
context: context,
|
||||
equipmentId: equipmentId,
|
||||
equipmentName: '${equipment.equipment.model?.vendor?.name ?? 'N/A'} ${equipment.equipment.serialNumber}', // Vendor + Equipment Number
|
||||
equipmentName: '${equipment.vendorName ?? 'N/A'} ${equipment.equipment.serialNumber}', // 백엔드 직접 제공 Vendor + Equipment Number
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
@@ -1413,5 +1571,69 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return options;
|
||||
}
|
||||
|
||||
/// 회사명 표시 텍스트 가져오기
|
||||
String _getCompanyDisplayText(int companyId) {
|
||||
// 캐시된 드롭다운 데이터에서 회사명 찾기
|
||||
if (_cachedDropdownData != null && _cachedDropdownData!['companies'] != null) {
|
||||
final companies = _cachedDropdownData!['companies'] as List<dynamic>;
|
||||
for (final company in companies) {
|
||||
if (company['id'] == companyId) {
|
||||
return company['name'] ?? '알수없는 회사';
|
||||
}
|
||||
}
|
||||
}
|
||||
return '회사 #$companyId';
|
||||
}
|
||||
|
||||
/// 소유회사별 필터 드롭다운 옵션 생성
|
||||
List<ShadOption<int?>> _buildCompanySelectOptions() {
|
||||
List<ShadOption<int?>> options = [
|
||||
const ShadOption(value: null, child: Text('전체 소유회사')),
|
||||
];
|
||||
|
||||
// 캐시된 드롭다운 데이터에서 회사 목록 가져오기
|
||||
if (_cachedDropdownData != null && _cachedDropdownData!['companies'] != null) {
|
||||
final companies = _cachedDropdownData!['companies'] as List<dynamic>;
|
||||
|
||||
for (final company in companies) {
|
||||
final id = company['id'] as int?;
|
||||
final name = company['name'] as String?;
|
||||
|
||||
if (id != null && name != null) {
|
||||
options.add(
|
||||
ShadOption(
|
||||
value: id,
|
||||
child: Text(name),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
// 사용하지 않는 현재위치, 점검일 관련 함수들 제거됨 (리스트 API에서 제공하지 않음)
|
||||
|
||||
/// 장비 고급 검색 다이얼로그 표시
|
||||
void _showEquipmentSearchDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => EquipmentSearchDialog(
|
||||
onEquipmentFound: (equipment) {
|
||||
// 검색된 장비를 상세보기로 이동 또는 다른 처리
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast(
|
||||
title: const Text('장비 검색 완료'),
|
||||
description: Text('${equipment.serialNumber} 장비를 찾았습니다.'),
|
||||
),
|
||||
);
|
||||
// 필요하면 검색된 장비의 상세정보로 이동
|
||||
// _onEditTap(equipment);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user