feat: 대규모 코드베이스 개선 - 백엔드 통합성 강화 및 UI 일관성 완성
- CLAUDE.md 대폭 개선: 개발 가이드라인 및 프로젝트 상태 문서화 - 백엔드 API 통합: 모든 엔티티 간 Foreign Key 관계 완벽 구현 - UI 일관성 강화: shadcn_ui 컴포넌트 표준화 적용 - 데이터 모델 개선: DTO 및 모델 클래스 백엔드 스키마와 100% 일치 - 사용자 관리: 회사 연결, 중복 검사, 입력 검증 기능 추가 - 창고 관리: 우편번호 연결, 중복 검사 기능 강화 - 회사 관리: 우편번호 연결, 중복 검사 로직 구현 - 장비 관리: 불필요한 카테고리 필드 제거, 벤더-모델 관계 정리 - 우편번호 시스템: 검색 다이얼로그 Provider 버그 수정 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/models/address_model.dart';
|
||||
import 'package:superport/screens/common/templates/form_layout_template.dart';
|
||||
import 'package:superport/screens/company/controllers/company_form_controller.dart';
|
||||
import 'package:superport/utils/validators.dart';
|
||||
import 'package:superport/utils/formatters/korean_phone_formatter.dart';
|
||||
import 'package:superport/data/models/zipcode_dto.dart';
|
||||
import 'package:superport/screens/zipcode/zipcode_search_screen.dart';
|
||||
import 'package:superport/screens/zipcode/controllers/zipcode_controller.dart';
|
||||
import 'package:superport/domain/usecases/zipcode_usecase.dart';
|
||||
|
||||
/// 회사 등록/수정 화면
|
||||
/// User/Warehouse Location 화면과 동일한 FormFieldWrapper 패턴 사용
|
||||
@@ -23,6 +29,11 @@ class _CompanyFormScreenState extends State<CompanyFormScreen> {
|
||||
final TextEditingController _phoneNumberController = TextEditingController();
|
||||
int? companyId;
|
||||
bool isBranch = false;
|
||||
|
||||
// 중복 검사 상태 관리
|
||||
bool _isCheckingDuplicate = false;
|
||||
String _duplicateCheckMessage = '';
|
||||
Color _messageColor = Colors.transparent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -69,12 +80,78 @@ class _CompanyFormScreenState extends State<CompanyFormScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 우편번호 검색 다이얼로그
|
||||
Future<ZipcodeDto?> _showZipcodeSearchDialog() async {
|
||||
return await showDialog<ZipcodeDto>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext dialogContext) => Dialog(
|
||||
clipBehavior: Clip.none,
|
||||
insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 24),
|
||||
child: SizedBox(
|
||||
width: 800,
|
||||
height: 600,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ShadTheme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ChangeNotifierProvider(
|
||||
create: (_) => ZipcodeController(
|
||||
GetIt.instance<ZipcodeUseCase>(),
|
||||
),
|
||||
child: ZipcodeSearchScreen(
|
||||
onSelect: (zipcode) {
|
||||
Navigator.of(dialogContext).pop(zipcode);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 회사 저장
|
||||
Future<void> _saveCompany() async {
|
||||
if (!_controller.formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 저장 시점에 중복 검사 수행
|
||||
final companyName = _controller.nameController.text.trim();
|
||||
if (companyName.isEmpty) {
|
||||
setState(() {
|
||||
_duplicateCheckMessage = '회사명을 입력하세요';
|
||||
_messageColor = Colors.red;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 중복 검사 시작
|
||||
setState(() {
|
||||
_isCheckingDuplicate = true;
|
||||
_duplicateCheckMessage = '회사명 중복 확인 중...';
|
||||
_messageColor = Colors.blue;
|
||||
});
|
||||
|
||||
final isDuplicate = await _controller.checkDuplicateName(companyName);
|
||||
|
||||
if (isDuplicate) {
|
||||
setState(() {
|
||||
_isCheckingDuplicate = false;
|
||||
_duplicateCheckMessage = '이미 존재하는 회사명입니다';
|
||||
_messageColor = Colors.red;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isCheckingDuplicate = false;
|
||||
_duplicateCheckMessage = '사용 가능한 회사명입니다';
|
||||
_messageColor = Colors.green;
|
||||
});
|
||||
|
||||
// 주소 업데이트
|
||||
_controller.updateCompanyAddress(
|
||||
Address.fromFullAddress(_addressController.text)
|
||||
@@ -235,29 +312,79 @@ class _CompanyFormScreenState extends State<CompanyFormScreen> {
|
||||
// 회사명 (필수)
|
||||
FormFieldWrapper(
|
||||
label: "회사명 *",
|
||||
child: ShadInputFormField(
|
||||
controller: _controller.nameController,
|
||||
placeholder: const Text('회사명을 입력하세요'),
|
||||
validator: (value) {
|
||||
if (value.trim().isEmpty) {
|
||||
return '회사명을 입력하세요';
|
||||
}
|
||||
if (value.trim().length < 2) {
|
||||
return '회사명은 2자 이상 입력하세요';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShadInputFormField(
|
||||
controller: _controller.nameController,
|
||||
placeholder: const Text('회사명을 입력하세요'),
|
||||
validator: (value) {
|
||||
if (value.trim().isEmpty) {
|
||||
return '회사명을 입력하세요';
|
||||
}
|
||||
if (value.trim().length < 2) {
|
||||
return '회사명은 2자 이상 입력하세요';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
// 중복 검사 메시지 영역 (고정 높이)
|
||||
SizedBox(
|
||||
height: 24,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
_duplicateCheckMessage,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _messageColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 우편번호 검색
|
||||
FormFieldWrapper(
|
||||
label: "우편번호",
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShadInputFormField(
|
||||
controller: _controller.zipcodeController,
|
||||
placeholder: const Text('우편번호'),
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton(
|
||||
onPressed: () async {
|
||||
// 우편번호 검색 다이얼로그 호출
|
||||
final result = await _showZipcodeSearchDialog();
|
||||
if (result != null) {
|
||||
_controller.selectZipcode(result);
|
||||
// 주소 필드도 업데이트
|
||||
_addressController.text = '${result.sido} ${result.gu} ${result.etc ?? ''}'.trim();
|
||||
}
|
||||
},
|
||||
child: const Text('검색'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 주소 (선택)
|
||||
FormFieldWrapper(
|
||||
label: "주소",
|
||||
child: ShadInputFormField(
|
||||
controller: _addressController,
|
||||
placeholder: const Text('회사 주소를 입력하세요'),
|
||||
placeholder: const Text('상세 주소를 입력하세요'),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
@@ -340,7 +467,7 @@ class _CompanyFormScreenState extends State<CompanyFormScreen> {
|
||||
|
||||
// 저장 버튼
|
||||
ShadButton(
|
||||
onPressed: _saveCompany,
|
||||
onPressed: _isCheckingDuplicate ? null : _saveCompany,
|
||||
size: ShadButtonSize.lg,
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
|
||||
Reference in New Issue
Block a user