832 lines
32 KiB
Dart
832 lines
32 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
|
import 'package:superport/models/equipment_unified_model.dart';
|
|
import 'package:superport/models/company_model.dart';
|
|
import 'package:superport/models/company_branch_info.dart';
|
|
import 'package:superport/screens/common/custom_widgets.dart';
|
|
import 'package:superport/screens/common/theme_shadcn.dart';
|
|
import 'package:superport/screens/equipment/controllers/equipment_out_form_controller.dart';
|
|
import 'package:superport/screens/equipment/widgets/equipment_summary_card.dart';
|
|
import 'package:superport/screens/common/widgets/remark_input.dart';
|
|
|
|
class EquipmentOutFormScreen extends StatefulWidget {
|
|
final int? equipmentOutId;
|
|
final Equipment? selectedEquipment;
|
|
final int? selectedEquipmentInId;
|
|
final List<Map<String, dynamic>>? selectedEquipments;
|
|
|
|
const EquipmentOutFormScreen({
|
|
super.key,
|
|
this.equipmentOutId,
|
|
this.selectedEquipment,
|
|
this.selectedEquipmentInId,
|
|
this.selectedEquipments,
|
|
});
|
|
|
|
@override
|
|
State<EquipmentOutFormScreen> createState() => _EquipmentOutFormScreenState();
|
|
}
|
|
|
|
class _EquipmentOutFormScreenState extends State<EquipmentOutFormScreen> {
|
|
late final EquipmentOutFormController _controller;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = EquipmentOutFormController();
|
|
_controller.isEditMode = widget.equipmentOutId != null;
|
|
_controller.equipmentOutId = widget.equipmentOutId;
|
|
_controller.selectedEquipment = widget.selectedEquipment;
|
|
_controller.selectedEquipmentInId = widget.selectedEquipmentInId;
|
|
_controller.selectedEquipments = widget.selectedEquipments;
|
|
_controller.loadDropdownData();
|
|
if (_controller.isEditMode) {
|
|
// 수정 모드: 기존 출고 정보 로드
|
|
// (이 부분은 실제 서비스에서 컨트롤러에 메서드 추가 필요)
|
|
} else if (widget.selectedEquipments != null &&
|
|
widget.selectedEquipments!.isNotEmpty) {
|
|
// 다중 선택 장비 있음: 별도 초기화 필요시 컨트롤러에서 처리
|
|
} else if (widget.selectedEquipment != null) {
|
|
_controller.initializeWithSelectedEquipment(widget.selectedEquipment!);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
// 요약 테이블 위젯 - 다중 선택 장비에 대한 요약 테이블
|
|
Widget _buildSummaryTable(EquipmentOutFormController controller) {
|
|
if (controller.selectedEquipments == null ||
|
|
controller.selectedEquipments!.isEmpty) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
// 각 장비별로 전체 폭을 사용하는 리스트로 구현
|
|
return SizedBox(
|
|
width: double.infinity, // 전체 폭 사용
|
|
child: ShadCard(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'선택된 장비 목록 (${controller.selectedEquipments!.length}개)',
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
// 리스트 헤더
|
|
Row(
|
|
children: const [
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
'제조사',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
'장비명',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 1,
|
|
child: Text(
|
|
'수량',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
'워런티 시작일',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
'워런티 종료일',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const Divider(),
|
|
// 리스트 본문
|
|
Column(
|
|
children: List.generate(controller.selectedEquipments!.length, (
|
|
index,
|
|
) {
|
|
final equipmentData = controller.selectedEquipments![index];
|
|
final equipment = equipmentData['equipment'] as Equipment;
|
|
// 워런티 날짜를 임시로 저장할 수 있도록 상태를 관리(컨트롤러에 리스트로 추가하거나, 여기서 임시로 관리)
|
|
// 여기서는 equipment 객체의 필드를 직접 수정(실제 서비스에서는 별도 상태 관리 필요)
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
|
child: Row(
|
|
children: [
|
|
Expanded(flex: 2, child: Text(equipment.manufacturer)),
|
|
Expanded(flex: 2, child: Text(equipment.name)),
|
|
Expanded(flex: 1, child: Text('${equipment.quantity}')),
|
|
Expanded(
|
|
flex: 2,
|
|
child: InkWell(
|
|
onTap: () async {
|
|
final picked = await showDatePicker(
|
|
context: context,
|
|
initialDate:
|
|
equipment.warrantyStartDate ??
|
|
DateTime.now(),
|
|
firstDate: DateTime(2000),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (picked != null) {
|
|
equipment.warrantyStartDate = picked;
|
|
// controller.notifyListeners() 호출 불필요 - 데이터 변경 시 자동 알림
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 8,
|
|
horizontal: 4,
|
|
),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
_formatDate(equipment.warrantyStartDate),
|
|
style: const TextStyle(
|
|
decoration: TextDecoration.underline,
|
|
color: Colors.blue,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: InkWell(
|
|
onTap: () async {
|
|
final picked = await showDatePicker(
|
|
context: context,
|
|
initialDate:
|
|
equipment.warrantyEndDate ?? DateTime.now(),
|
|
firstDate: DateTime(2000),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (picked != null) {
|
|
equipment.warrantyEndDate = picked;
|
|
// controller.notifyListeners() 호출 불필요 - 데이터 변경 시 자동 알림
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 8,
|
|
horizontal: 4,
|
|
),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
_formatDate(equipment.warrantyEndDate),
|
|
style: const TextStyle(
|
|
decoration: TextDecoration.underline,
|
|
color: Colors.blue,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 날짜 포맷 유틸리티
|
|
String _formatDate(DateTime? date) {
|
|
if (date == null) return '정보 없음';
|
|
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ChangeNotifierProvider.value(
|
|
value: _controller,
|
|
child: Consumer<EquipmentOutFormController>(
|
|
builder: (context, controller, child) {
|
|
// 담당자가 없거나 첫 번째 회사에 대한 담당자가 '없음'인 경우 등록 버튼 비활성화 조건
|
|
final bool canSubmit =
|
|
controller.selectedCompanies.isNotEmpty &&
|
|
controller.selectedCompanies[0] != null &&
|
|
controller.hasManagersPerCompany[0] &&
|
|
controller.filteredManagersPerCompany[0].first != '없음';
|
|
final int totalSelectedEquipments =
|
|
controller.selectedEquipments?.length ?? 0;
|
|
|
|
// 로딩 상태 처리
|
|
if (controller.isLoading) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('장비 출고'),
|
|
),
|
|
body: const Center(
|
|
child: ShadProgress(),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 에러 상태 처리
|
|
if (controller.errorMessage != null) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('장비 출고'),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
Icons.error_outline,
|
|
size: 64,
|
|
color: Colors.red.shade400,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'오류가 발생했습니다',
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
controller.errorMessage!,
|
|
style: TextStyle(color: Colors.grey.shade600),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 24),
|
|
ShadButton(
|
|
onPressed: () {
|
|
controller.clearError();
|
|
controller.loadDropdownData();
|
|
},
|
|
child: const Text('다시 시도'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(
|
|
controller.isEditMode
|
|
? '장비 출고 수정'
|
|
: totalSelectedEquipments > 0
|
|
? '장비 출고 등록 ($totalSelectedEquipments개)'
|
|
: '장비 출고 등록',
|
|
),
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Form(
|
|
key: controller.formKey,
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 장비 정보 요약 섹션
|
|
if (controller.selectedEquipments != null &&
|
|
controller.selectedEquipments!.isNotEmpty)
|
|
_buildSummaryTable(controller)
|
|
else if (controller.selectedEquipment != null)
|
|
// 단일 장비 요약 카드도 전체 폭으로 맞춤
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: EquipmentSingleSummaryCard(
|
|
equipment: controller.selectedEquipment!,
|
|
),
|
|
)
|
|
else
|
|
const SizedBox.shrink(),
|
|
// 요약 카드 아래 라디오 버튼 추가
|
|
const SizedBox(height: 12),
|
|
// 전체 폭을 사용하는 라디오 버튼
|
|
SizedBox(width: double.infinity, child: _buildOutTypeRadio(controller)),
|
|
const SizedBox(height: 16),
|
|
// 출고 정보 입력 섹션 (수정/등록)
|
|
_buildOutgoingInfoSection(context, controller),
|
|
// 비고 입력란 추가
|
|
const SizedBox(height: 16),
|
|
FormFieldWrapper(
|
|
label: '비고',
|
|
isRequired: false,
|
|
child: RemarkInput(
|
|
controller: controller.remarkController,
|
|
hint: '비고를 입력하세요',
|
|
minLines: 4,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
// 담당자 없음 경고 메시지
|
|
if (controller.selectedCompanies.isNotEmpty &&
|
|
controller.selectedCompanies[0] != null &&
|
|
(!controller.hasManagersPerCompany[0] ||
|
|
controller.filteredManagersPerCompany[0].first ==
|
|
'없음'))
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.red.shade100,
|
|
borderRadius: BorderRadius.circular(4),
|
|
border: Border.all(color: Colors.red.shade300),
|
|
),
|
|
child: const Row(
|
|
children: [
|
|
Icon(Icons.warning, color: Colors.red),
|
|
SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
'선택한 회사에 등록된 담당자가 없습니다. 담당자를 먼저 등록해야 합니다.',
|
|
style: TextStyle(color: Colors.red),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
// 저장 버튼
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ShadButton(
|
|
onPressed:
|
|
canSubmit
|
|
? () {
|
|
// 각 회사별 담당자를 첫 번째 항목으로 설정
|
|
for (
|
|
int i = 0;
|
|
i < controller.selectedCompanies.length;
|
|
i++
|
|
) {
|
|
if (controller.selectedCompanies[i] != null &&
|
|
controller.hasManagersPerCompany[i] &&
|
|
controller
|
|
.filteredManagersPerCompany[i]
|
|
.isNotEmpty &&
|
|
_controller
|
|
.filteredManagersPerCompany[i]
|
|
.first !=
|
|
'없음') {
|
|
controller.selectedManagersPerCompany[i] =
|
|
controller
|
|
.filteredManagersPerCompany[i]
|
|
.first;
|
|
}
|
|
}
|
|
|
|
controller.saveEquipmentOut(context).then((success) {
|
|
if (success) {
|
|
ShadToaster.of(context).show(
|
|
ShadToast(
|
|
title: const Text('출고 완료'),
|
|
description: const Text('출고가 완료되었습니다.'),
|
|
),
|
|
);
|
|
Navigator.pop(context, true);
|
|
}
|
|
});
|
|
}
|
|
: null,
|
|
child: Text(
|
|
controller.isEditMode ? '수정하기' : '등록하기',
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// 출고 정보 입력 섹션 위젯 (등록/수정 공통)
|
|
Widget _buildOutgoingInfoSection(BuildContext context, EquipmentOutFormController controller) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('출고 정보', style: Theme.of(context).textTheme.titleMedium),
|
|
const SizedBox(height: 12),
|
|
// 출고일
|
|
_buildDateField(
|
|
context,
|
|
controller,
|
|
label: '출고일',
|
|
date: controller.outDate,
|
|
onDateChanged: (picked) {
|
|
controller.outDate = picked;
|
|
},
|
|
),
|
|
|
|
// 장비 상태 변경 (출고 시 'inuse'로 자동 설정)
|
|
const Text('장비 상태 설정', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 4),
|
|
ShadSelect<String>(
|
|
initialValue: 'inuse', // 출고 시 기본값
|
|
placeholder: const Text('출고 후 장비 상태'),
|
|
selectedOptionBuilder: (context, value) => Text(
|
|
value == 'inuse' ? '사용 중' : '유지보수',
|
|
),
|
|
options: const [
|
|
ShadOption(value: 'inuse', child: Text('사용 중')),
|
|
ShadOption(value: 'maintenance', child: Text('유지보수')),
|
|
],
|
|
onChanged: (value) {
|
|
// controller.equipmentStatus = value; // TODO: 컨트롤러에 추가 필요
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// 출고 회사 영역 헤더
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text('출고 회사 *', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
TextButton.icon(
|
|
onPressed: () {
|
|
controller.addCompany();
|
|
},
|
|
icon: const Icon(Icons.add_circle_outline, size: 18),
|
|
label: const Text('출고 회사 추가'),
|
|
style: TextButton.styleFrom(
|
|
padding: EdgeInsets.zero,
|
|
minimumSize: Size.zero,
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
|
|
// 동적 출고 회사 드롭다운 목록
|
|
...List.generate(controller.selectedCompanies.length, (index) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12.0),
|
|
child: ShadSelect<String>(
|
|
initialValue: controller.selectedCompanies[index],
|
|
placeholder: Text(index == 0 ? '출고할 회사를 선택하세요' : '추가된 출고할 회사를 선택하세요'),
|
|
enabled:
|
|
index == 0 ||
|
|
controller.selectedCompanies[index - 1] != null,
|
|
selectedOptionBuilder: (context, value) =>
|
|
_buildCompanyDropdownItem(value ?? '', controller),
|
|
options:
|
|
controller.availableCompaniesPerDropdown[index]
|
|
.map(
|
|
(item) => ShadOption(
|
|
value: item.name,
|
|
child: _buildCompanyDropdownItem(item.name, controller),
|
|
),
|
|
)
|
|
.toList(),
|
|
onChanged:
|
|
(index == 0 ||
|
|
controller.selectedCompanies[index - 1] != null)
|
|
? (value) {
|
|
controller.selectedCompanies[index] = value;
|
|
controller.filterManagersByCompanyAtIndex(index);
|
|
controller.updateAvailableCompanies();
|
|
}
|
|
: null,
|
|
),
|
|
);
|
|
}),
|
|
|
|
// 각 회사별 담당자 선택 목록
|
|
...List.generate(controller.selectedCompanies.length, (index) {
|
|
// 회사가 선택된 경우에만 담당자 표시
|
|
if (controller.selectedCompanies[index] != null) {
|
|
// 회사 정보 가져오기
|
|
final companyInfo = controller.companiesWithBranches.firstWhere(
|
|
(info) => info.name == controller.selectedCompanies[index],
|
|
orElse:
|
|
() => CompanyBranchInfo(
|
|
id: 0,
|
|
name: controller.selectedCompanies[index]!,
|
|
originalName: controller.selectedCompanies[index]!,
|
|
isMainCompany: true,
|
|
companyId: 0,
|
|
branchId: null,
|
|
),
|
|
);
|
|
|
|
// 실제 회사/지점 정보를 ID로 가져오기
|
|
Company? company;
|
|
Branch? branch;
|
|
|
|
if (companyInfo.companyId != null) {
|
|
// TODO: 실제 CompanyService를 통해 회사 정보 가져오기
|
|
// company = await _companyService.getCompanyById(companyInfo.companyId!);
|
|
company = null; // 임시로 null 처리
|
|
if (!companyInfo.isMainCompany &&
|
|
companyInfo.branchId != null &&
|
|
company != null) {
|
|
final branches = company.branches;
|
|
if (branches != null) {
|
|
branch = branches.firstWhere(
|
|
(b) => b.id == companyInfo.branchId,
|
|
orElse:
|
|
() => Branch(
|
|
companyId: companyInfo.companyId!,
|
|
name: companyInfo.originalName,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'담당자 정보 (${controller.selectedCompanies[index]})',
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 15,
|
|
),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey.shade400),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child:
|
|
company != null
|
|
? Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 본사/지점 정보 표시
|
|
if (companyInfo.isMainCompany &&
|
|
company.contactName != null &&
|
|
company.contactName!.isNotEmpty)
|
|
Text(
|
|
'${company.contactName} ${company.contactPosition ?? ""} ${company.contactPhone ?? ""} ${company.contactEmail ?? ""}',
|
|
style: ShadcnTheme.bodyMedium,
|
|
),
|
|
if (!companyInfo.isMainCompany &&
|
|
branch != null &&
|
|
branch.contactName != null &&
|
|
branch.contactName!.isNotEmpty)
|
|
Text(
|
|
'${branch.contactName} ${branch.contactPosition ?? ""} ${branch.contactPhone ?? ""} ${branch.contactEmail ?? ""}',
|
|
style: ShadcnTheme.bodyMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
// 담당자 목록에서 실제 담당자 정보만 표시하는 부분은 제거
|
|
],
|
|
)
|
|
: Text(
|
|
'회사 정보를 불러올 수 없습니다.',
|
|
style: TextStyle(
|
|
color: Colors.red.shade400,
|
|
fontStyle: FontStyle.italic,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} else {
|
|
return const SizedBox.shrink();
|
|
}
|
|
}),
|
|
|
|
// 유지 보수(라이센스) 선택
|
|
_buildDropdownField(
|
|
label: '유지 보수', // 텍스트 변경
|
|
value: controller.selectedLicense,
|
|
items: controller.licenses,
|
|
hint: '유지 보수를 선택하세요', // 텍스트 변경
|
|
onChanged: (value) {
|
|
controller.selectedLicense = value;
|
|
},
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return '유지 보수를 선택해주세요'; // 텍스트 변경
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 날짜 선택 필드 위젯
|
|
Widget _buildDateField(
|
|
BuildContext context,
|
|
EquipmentOutFormController controller, {
|
|
required String label,
|
|
required DateTime date,
|
|
required ValueChanged<DateTime> onDateChanged,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 4),
|
|
InkWell(
|
|
onTap: () async {
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: date,
|
|
firstDate: DateTime(2000),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (picked != null && picked != date) {
|
|
onDateChanged(picked);
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 15),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey.shade400),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
controller.formatDate(date),
|
|
style: ShadcnTheme.bodyMedium,
|
|
),
|
|
const Icon(Icons.calendar_today, size: 20),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 드롭다운 필드 위젯
|
|
Widget _buildDropdownField({
|
|
required String label,
|
|
required String? value,
|
|
required List<String> items,
|
|
required String hint,
|
|
required ValueChanged<String?>? onChanged,
|
|
required String? Function(String?) validator,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 4),
|
|
ShadSelect<String>(
|
|
initialValue: value,
|
|
placeholder: Text(hint),
|
|
selectedOptionBuilder: (context, value) => Text(value ?? ''),
|
|
options:
|
|
items
|
|
.map(
|
|
(item) => ShadOption(
|
|
value: item,
|
|
child: Text(item),
|
|
),
|
|
)
|
|
.toList(),
|
|
onChanged: onChanged,
|
|
),
|
|
const SizedBox(height: 12),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 회사 이름을 표시하는 위젯 (지점 포함)
|
|
Widget _buildCompanyDropdownItem(String item, EquipmentOutFormController controller) {
|
|
final TextStyle defaultStyle = TextStyle(
|
|
color: Colors.black87,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.normal,
|
|
);
|
|
|
|
// 컨트롤러에서 해당 항목에 대한 정보 확인
|
|
final companyInfoList =
|
|
controller.companiesWithBranches
|
|
.where((info) => info.name == item)
|
|
.toList();
|
|
|
|
// 회사 정보가 존재하고 지점인 경우
|
|
if (companyInfoList.isNotEmpty && !companyInfoList[0].isMainCompany) {
|
|
final companyInfo = companyInfoList[0];
|
|
final parentCompanyName = companyInfo.parentCompanyName ?? '';
|
|
final branchName = companyInfo.displayName ?? companyInfo.originalName;
|
|
|
|
// Row 대신 RichText 사용 - 지점 표시
|
|
return RichText(
|
|
text: TextSpan(
|
|
style: defaultStyle, // 기본 스타일 설정
|
|
children: [
|
|
WidgetSpan(
|
|
child: Icon(
|
|
Icons.subdirectory_arrow_right,
|
|
size: 16,
|
|
color: Colors.grey,
|
|
),
|
|
alignment: PlaceholderAlignment.middle,
|
|
),
|
|
TextSpan(text: ' ', style: defaultStyle),
|
|
TextSpan(
|
|
text: parentCompanyName, // 회사명
|
|
style: defaultStyle,
|
|
),
|
|
TextSpan(text: ' ', style: defaultStyle),
|
|
TextSpan(
|
|
text: branchName, // 지점명
|
|
style: const TextStyle(
|
|
color: Colors.indigo,
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
);
|
|
}
|
|
|
|
// 일반 회사명 (본사)
|
|
return RichText(
|
|
text: TextSpan(
|
|
style: defaultStyle, // 기본 스타일 설정
|
|
children: [
|
|
WidgetSpan(
|
|
child: Icon(Icons.business, size: 16, color: Colors.black54),
|
|
alignment: PlaceholderAlignment.middle,
|
|
),
|
|
TextSpan(text: ' ', style: defaultStyle),
|
|
TextSpan(
|
|
text: item,
|
|
style: defaultStyle.copyWith(fontWeight: FontWeight.w500),
|
|
),
|
|
],
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
);
|
|
}
|
|
|
|
|
|
// 출고/대여/폐기 라디오 버튼 위젯
|
|
Widget _buildOutTypeRadio(EquipmentOutFormController controller) {
|
|
// 출고 유형 리스트
|
|
final List<String> outTypes = ['출고', '대여', '폐기'];
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children:
|
|
outTypes.map((type) {
|
|
return Row(
|
|
children: [
|
|
Radio<String>(
|
|
value: type,
|
|
groupValue: controller.outType, // 컨트롤러에서 현재 선택값 관리
|
|
onChanged: (value) {
|
|
controller.outType = value!;
|
|
},
|
|
),
|
|
Text(type),
|
|
],
|
|
);
|
|
}).toList(),
|
|
);
|
|
}
|
|
}
|