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:
JiWoong Sul
2025-09-02 19:51:40 +09:00
parent 650cd4be55
commit c419f8f458
149 changed files with 12934 additions and 3644 deletions

View File

@@ -21,6 +21,7 @@ class EquipmentInFormScreen extends StatefulWidget {
class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
late EquipmentInFormController _controller;
late TextEditingController _serialNumberController;
late TextEditingController _barcodeController;
late TextEditingController _initialStockController;
late TextEditingController _purchasePriceController;
Future<void>? _initFuture;
@@ -49,6 +50,7 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
// TextEditingController 초기화
_serialNumberController = TextEditingController(text: _controller.serialNumber);
_barcodeController = TextEditingController(text: _controller.barcode);
_initialStockController = TextEditingController(text: _controller.initialStock.toString());
_purchasePriceController = TextEditingController(
text: _controller.purchasePrice != null
@@ -62,6 +64,7 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
// 데이터 로드 후 컨트롤러 업데이트
setState(() {
_serialNumberController.text = _controller.serialNumber;
_barcodeController.text = _controller.barcode;
_purchasePriceController.text = _controller.purchasePrice != null
? CurrencyFormatter.formatKRW(_controller.purchasePrice)
: '';
@@ -73,7 +76,7 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
_controller.removeListener(_onControllerUpdated);
_controller.dispose();
_serialNumberController.dispose();
_serialNumberController.dispose();
_barcodeController.dispose();
_initialStockController.dispose();
_purchasePriceController.dispose();
super.dispose();
@@ -167,6 +170,8 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
const SizedBox(height: 24),
_buildPurchaseSection(),
const SizedBox(height: 24),
_buildWarrantySection(),
const SizedBox(height: 24),
_buildRemarkSection(),
],
),
@@ -183,6 +188,7 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'기본 정보',
@@ -192,7 +198,22 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
),
const SizedBox(height: 16),
// 장비 번호 (필수)
// 1. 제조사/모델 정보 (수정 모드: 읽기 전용, 생성 모드: 선택)
if (_controller.isEditMode)
..._buildReadOnlyVendorModel()
else
Container(
width: double.infinity,
child: EquipmentVendorModelSelector(
initialVendorId: _controller.vendorId,
initialModelId: _controller.modelsId,
onChanged: _controller.onVendorModelChanged,
isReadOnly: false,
),
),
const SizedBox(height: 16),
// 2. 장비 번호 (필수)
ShadInputFormField(
controller: _serialNumberController,
readOnly: _controller.isFieldReadOnly('serialNumber'),
@@ -202,40 +223,27 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
label: Text(_controller.isFieldReadOnly('serialNumber')
? '장비 번호 * 🔒' : '장비 번호 *'),
validator: (value) {
if (value.trim().isEmpty ?? true) {
if (value?.trim().isEmpty ?? true) {
return '장비 번호는 필수입니다';
}
return null;
},
onChanged: _controller.isFieldReadOnly('serialNumber') ? null : (value) {
_controller.serialNumber = value.trim() ?? '';
_controller.serialNumber = value?.trim() ?? '';
setState(() {});
print('DEBUG [장비번호 입력] value: "$value", controller.serialNumber: "${_controller.serialNumber}"');
},
),
const SizedBox(height: 16),
// Vendor→Model cascade 선택기
EquipmentVendorModelSelector(
initialVendorId: _controller.vendorId,
initialModelId: _controller.modelsId,
onChanged: _controller.onVendorModelChanged,
isReadOnly: _controller.isFieldReadOnly('modelsId'),
),
const SizedBox(height: 16),
// 시리얼 번호 (선택)
// 3. 바코드 (선택사항)
ShadInputFormField(
controller: _serialNumberController,
readOnly: _controller.isFieldReadOnly('serialNumber'),
placeholder: Text(_controller.isFieldReadOnly('serialNumber')
? '수정불가' : '시리얼 번호를 입력하세요'),
label: Text(_controller.isFieldReadOnly('serialNumber')
? '시리얼 번호 🔒' : '시리얼 번호'),
onChanged: _controller.isFieldReadOnly('serialNumber') ? null : (value) {
_controller.serialNumber = value.trim() ?? '';
setState(() {});
print('DEBUG [시리얼번호 입력] value: "$value", controller.serialNumber: "${_controller.serialNumber}"');
controller: _barcodeController,
placeholder: const Text('바코드를 입력하세요'),
label: const Text('바코드'),
onChanged: (value) {
_controller.barcode = value?.trim() ?? '';
print('DEBUG [바코드 입력] value: "$value", controller.barcode: "${_controller.barcode}"');
},
),
],
@@ -260,30 +268,32 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
),
const SizedBox(height: 16),
// 구매처 (드롭다운 전용)
ShadSelect<int>(
initialValue: _getValidCompanyId(),
placeholder: const Text('구매처를 선택하세요'),
options: _controller.companies.entries.map((entry) =>
ShadOption(
value: entry.key,
child: Text(entry.value),
)
).toList(),
selectedOptionBuilder: (context, value) {
// companies가 비어있거나 해당 value가 없는 경우 처리
if (_controller.companies.isEmpty) {
return const Text('로딩중...');
}
return Text(_controller.companies[value] ?? '선택하세요');
},
onChanged: (value) {
setState(() {
_controller.selectedCompanyId = value;
});
print('DEBUG [구매처 선택] value: $value, companies: ${_controller.companies.length}');
},
),
// 구매처 (수정 모드: 읽기 전용, 생성 모드: 선택)
if (_controller.isEditMode)
_buildReadOnlyCompany()
else
ShadSelect<int>(
initialValue: _getValidCompanyId(),
placeholder: const Text('구매처를 선택하세요'),
options: _controller.companies.entries.map((entry) =>
ShadOption(
value: entry.key,
child: Text(entry.value),
)
).toList(),
selectedOptionBuilder: (context, value) {
if (_controller.companies.isEmpty) {
return const Text('로딩중...');
}
return Text(_controller.companies[value] ?? '선택하세요');
},
onChanged: (value) {
setState(() {
_controller.selectedCompanyId = value;
});
print('DEBUG [구매처 선택] value: $value, companies: ${_controller.companies.length}');
},
),
const SizedBox(height: 16),
// 입고지 (드롭다운 전용)
@@ -396,7 +406,7 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
Text(
_controller.purchaseDate != null
? '${_controller.purchaseDate!.year}-${_controller.purchaseDate!.month.toString().padLeft(2, '0')}-${_controller.purchaseDate!.day.toString().padLeft(2, '0')}'
: _controller.isFieldReadOnly('purchaseDate') ? '구매일 미설정' : '날짜 선택',
: _controller.isFieldReadOnly('purchaseDate') ? '구매일 미설정' : '현재 날짜',
style: TextStyle(
color: _controller.isFieldReadOnly('purchaseDate')
? Colors.grey[600]
@@ -444,6 +454,140 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
);
}
Widget _buildWarrantySection() {
return ShadCard(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'워런티 정보 *',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
// 워런티 번호 (필수)
ShadInputFormField(
controller: _controller.warrantyNumberController,
label: const Text('워런티 번호 *'),
placeholder: const Text('워런티 번호를 입력하세요'),
validator: (value) {
if (value.trim().isEmpty ?? true) {
return '워런티 번호는 필수입니다';
}
return null;
},
),
const SizedBox(height: 16),
Row(
children: [
// 워런티 시작일 (필수)
Expanded(
child: InkWell(
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: _controller.warrantyStartDate,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (date != null) {
setState(() {
_controller.warrantyStartDate = date;
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${_controller.warrantyStartDate.year}-${_controller.warrantyStartDate.month.toString().padLeft(2, '0')}-${_controller.warrantyStartDate.day.toString().padLeft(2, '0')}',
),
const Icon(Icons.calendar_today, size: 16),
],
),
),
),
),
const SizedBox(width: 16),
// 워런티 만료일 (필수)
Expanded(
child: InkWell(
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: _controller.warrantyEndDate,
firstDate: _controller.warrantyStartDate,
lastDate: DateTime(2100),
);
if (date != null) {
setState(() {
_controller.warrantyEndDate = date;
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${_controller.warrantyEndDate.year}-${_controller.warrantyEndDate.month.toString().padLeft(2, '0')}-${_controller.warrantyEndDate.day.toString().padLeft(2, '0')}',
),
const Icon(Icons.calendar_today, size: 16),
],
),
),
),
),
],
),
const SizedBox(height: 16),
// 워런티 기간 표시
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Theme.of(context).dividerColor),
),
child: Row(
children: [
Icon(
Icons.info_outline,
size: 16,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
Text(
'워런티 기간: ${_controller.getWarrantyPeriodSummary()}',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
);
}
Widget _buildRemarkSection() {
return ShadCard(
child: Padding(
@@ -471,4 +615,43 @@ class _EquipmentInFormScreenState extends State<EquipmentInFormScreen> {
);
}
/// 읽기 전용 구매처 정보 표시 (백엔드 JOIN 데이터 활용)
Widget _buildReadOnlyCompany() {
// preloadedEquipment가 있으면 companyName 사용, 없으면 기본값
final companyName = _controller.preloadedEquipment?.companyName ??
(_controller.companies.isNotEmpty && _controller.selectedCompanyId != null
? _controller.companies[_controller.selectedCompanyId]
: '구매처 정보 없음');
return ShadInputFormField(
readOnly: true,
label: const Text('구매처 🔒'),
initialValue: companyName,
);
}
/// 읽기 전용 제조사/모델 정보 표시 (백엔드 JOIN 데이터 활용)
List<Widget> _buildReadOnlyVendorModel() {
return [
// 제조사 (읽기 전용)
ShadInputFormField(
readOnly: true,
label: const Text('제조사 * 🔒'),
initialValue: _controller.manufacturer.isNotEmpty
? _controller.manufacturer
: '제조사 정보 없음',
),
const SizedBox(height: 16),
// 모델 (읽기 전용)
ShadInputFormField(
readOnly: true,
label: const Text('모델 * 🔒'),
initialValue: _controller.name.isNotEmpty
? _controller.name
: '모델 정보 없음',
),
];
}
}