feat: 대규모 코드베이스 개선 - 백엔드 통합성 강화 및 UI 일관성 완성
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

- 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:
JiWoong Sul
2025-08-31 15:49:05 +09:00
parent 9dec6f1034
commit df7dd8dacb
46 changed files with 2148 additions and 2722 deletions

View File

@@ -30,12 +30,16 @@ class ZipcodeSearchFilter extends StatefulWidget {
class _ZipcodeSearchFilterState extends State<ZipcodeSearchFilter> {
final TextEditingController _searchController = TextEditingController();
final ScrollController _sidoScrollController = ScrollController();
final ScrollController _guScrollController = ScrollController();
Timer? _debounceTimer;
bool _hasFilters = false;
@override
void dispose() {
_searchController.dispose();
_sidoScrollController.dispose();
_guScrollController.dispose();
_debounceTimer?.cancel();
super.dispose();
}
@@ -51,12 +55,16 @@ class _ZipcodeSearchFilterState extends State<ZipcodeSearchFilter> {
}
void _onSidoChanged(String? value) {
widget.onSidoChanged(value);
// 빈 문자열을 null로 변환
final actualValue = (value == '') ? null : value;
widget.onSidoChanged(actualValue);
_updateHasFilters();
}
void _onGuChanged(String? value) {
widget.onGuChanged(value);
// 빈 문자열을 null로 변환
final actualValue = (value == '') ? null : value;
widget.onGuChanged(actualValue);
_updateHasFilters();
}
@@ -157,36 +165,45 @@ class _ZipcodeSearchFilterState extends State<ZipcodeSearchFilter> {
),
),
const SizedBox(height: 6),
SizedBox(
width: double.infinity,
child: ShadSelect<String>(
placeholder: const Text('시도 선택'),
onChanged: _onSidoChanged,
options: [
const ShadOption(
value: null,
child: Text('전체'),
widget.sidoList.isEmpty
? Container(
height: 38,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: theme.colorScheme.border),
borderRadius: BorderRadius.circular(6),
),
...widget.sidoList.map((sido) => ShadOption(
value: sido,
child: Text(sido),
)),
],
selectedOptionBuilder: (context, value) {
return Row(
children: [
Icon(
Icons.location_city,
size: 16,
color: theme.colorScheme.primary,
child: Text('로딩 중...', style: theme.textTheme.muted),
)
: SizedBox(
width: double.infinity,
child: ShadSelect<String>(
placeholder: const Text('시도 선택'),
maxHeight: 400,
shrinkWrap: true,
showScrollToBottomChevron: true,
showScrollToTopChevron: true,
scrollController: _sidoScrollController,
onChanged: (value) => _onSidoChanged(value),
options: [
const ShadOption(
value: '',
child: Text('전체'),
),
const SizedBox(width: 8),
Text(value ?? '전체'),
...widget.sidoList.map((sido) => ShadOption(
value: sido,
child: Text(sido),
)),
],
);
},
),
),
selectedOptionBuilder: (context, value) {
if (value == '') {
return const Text('전체');
}
return Text(value);
},
),
),
],
),
),
@@ -204,42 +221,45 @@ class _ZipcodeSearchFilterState extends State<ZipcodeSearchFilter> {
),
),
const SizedBox(height: 6),
SizedBox(
width: double.infinity,
child: ShadSelect<String>(
placeholder: Text(
widget.selectedSido == null
? '시도를 먼저 선택하세요'
: '구/군 선택'
),
onChanged: widget.selectedSido != null ? _onGuChanged : null,
options: [
const ShadOption(
value: null,
child: Text('전체'),
widget.selectedSido == null
? Container(
height: 38,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: theme.colorScheme.border),
borderRadius: BorderRadius.circular(6),
),
...widget.guList.map((gu) => ShadOption(
value: gu,
child: Text(gu),
)),
],
selectedOptionBuilder: (context, value) {
return Row(
children: [
Icon(
Icons.location_on,
size: 16,
color: widget.selectedSido != null
? theme.colorScheme.primary
: theme.colorScheme.mutedForeground,
child: Text('시도를 먼저 선택하세요', style: theme.textTheme.muted),
)
: SizedBox(
width: double.infinity,
child: ShadSelect<String>(
placeholder: const Text('구/군 선택'),
maxHeight: 400,
shrinkWrap: true,
showScrollToBottomChevron: true,
showScrollToTopChevron: true,
scrollController: _guScrollController,
onChanged: (value) => _onGuChanged(value),
options: [
const ShadOption(
value: '',
child: Text('전체'),
),
const SizedBox(width: 8),
Text(value ?? '전체'),
...widget.guList.map((gu) => ShadOption(
value: gu,
child: Text(gu),
)),
],
);
},
),
),
selectedOptionBuilder: (context, value) {
if (value == '') {
return const Text('전체');
}
return Text(value);
},
),
),
],
),
),

View File

@@ -128,9 +128,12 @@ class ZipcodeTable extends StatelessWidget {
color: theme.colorScheme.mutedForeground,
),
const SizedBox(width: 6),
Text(
zipcode.sido,
style: const TextStyle(fontWeight: FontWeight.w500),
Flexible(
child: Text(
zipcode.sido,
style: const TextStyle(fontWeight: FontWeight.w500),
overflow: TextOverflow.ellipsis,
),
),
],
),
@@ -146,9 +149,12 @@ class ZipcodeTable extends StatelessWidget {
color: theme.colorScheme.mutedForeground,
),
const SizedBox(width: 6),
Text(
zipcode.gu,
style: const TextStyle(fontWeight: FontWeight.w500),
Flexible(
child: Text(
zipcode.gu,
style: const TextStyle(fontWeight: FontWeight.w500),
overflow: TextOverflow.ellipsis,
),
),
],
),
@@ -190,28 +196,10 @@ class ZipcodeTable extends StatelessWidget {
// 작업
DataCell(
Row(
mainAxisSize: MainAxisSize.min,
children: [
ShadButton(
onPressed: () => onSelect(zipcode),
size: ShadButtonSize.sm,
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check, size: 14),
SizedBox(width: 4),
Text('선택'),
],
),
),
const SizedBox(width: 6),
ShadButton.outline(
onPressed: () => _showAddressDetails(context, zipcode),
size: ShadButtonSize.sm,
child: const Icon(Icons.info_outline, size: 14),
),
],
ShadButton(
onPressed: () => onSelect(zipcode),
size: ShadButtonSize.sm,
child: const Text('선택', style: TextStyle(fontSize: 11)),
),
),
],