import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/data/models/zipcode_dto.dart'; import 'package:superport/screens/zipcode/controllers/zipcode_controller.dart'; import 'package:superport/screens/zipcode/components/zipcode_search_filter.dart'; import 'package:superport/screens/zipcode/components/zipcode_table.dart'; class ZipcodeSearchScreen extends StatefulWidget { final Function(ZipcodeDto)? onSelect; // When used in a dialog/popup, you can cap the table width // by setting this fraction (0.0–1.0) of the screen width. final double? tableMaxWidthFraction; const ZipcodeSearchScreen({super.key, this.onSelect, this.tableMaxWidthFraction}); @override State createState() => _ZipcodeSearchScreenState(); } class _ZipcodeSearchScreenState extends State { late ZipcodeController _controller; @override void initState() { super.initState(); _controller = context.read(); // 위젯이 완전히 빌드된 후에 초기화 실행 WidgetsBinding.instance.addPostFrameCallback((_) { _controller.initialize(); }); } void _showSuccessToast(String message) { if (mounted) { ShadToaster.of(context).show( ShadToast( title: const Text('성공'), description: Text(message), duration: const Duration(seconds: 3), ), ); } } void _showErrorToast(String message) { if (mounted) { ShadToaster.of(context).show( ShadToast.destructive( title: Text('오류'), description: Text(message), duration: const Duration(seconds: 5), ), ); } } @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Consumer( builder: (context, controller, child) { // 에러 메시지 표시 if (controller.errorMessage != null) { WidgetsBinding.instance.addPostFrameCallback((_) { _showErrorToast(controller.errorMessage!); }); } return Material( color: theme.colorScheme.background, child: Column( children: [ // 헤더 섹션 Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: theme.colorScheme.card, border: Border( bottom: BorderSide( color: theme.colorScheme.border, width: 1, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 제목 및 설명 Row( children: [ Icon( Icons.location_on, size: 28, color: theme.colorScheme.primary, ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '우편번호 검색', style: theme.textTheme.h3, ), const SizedBox(height: 4), Text( '전국의 우편번호를 검색하고 정확한 주소를 찾을 수 있습니다', style: theme.textTheme.muted, ), ], ), const Spacer(), // 통계 정보 Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Text( '총 ${controller.totalCount}개 우편번호', style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), ), ), ], ), const SizedBox(height: 24), // 검색 및 필터 영역 ZipcodeSearchFilter( onSearch: controller.setSearchQuery, onSidoChanged: controller.setSido, onGuChanged: controller.setGu, sidoList: controller.sidoList, guList: controller.guList, selectedSido: controller.selectedSido, selectedGu: controller.selectedGu, ), ], ), ), // 메인 컨텐츠 영역 Expanded( child: Container( padding: const EdgeInsets.all(24), child: controller.isLoading && controller.zipcodes.isEmpty ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('우편번호를 검색하고 있습니다...'), ], ), ) : controller.zipcodes.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search_off, size: 64, color: theme.colorScheme.mutedForeground, ), const SizedBox(height: 16), Text( '검색 결과가 없습니다', style: theme.textTheme.h4, ), const SizedBox(height: 8), Text( '다른 검색어나 필터를 사용해 보세요', style: theme.textTheme.muted, ), const SizedBox(height: 24), ShadButton.outline( onPressed: controller.clearFilters, child: const Text('필터 초기화'), ), ], ), ) : Column( children: [ // 결과 정보 Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.colorScheme.muted.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( Icons.info_outline, size: 16, color: theme.colorScheme.mutedForeground, ), const SizedBox(width: 8), Text( '총 ${controller.totalCount}개 중 ${controller.zipcodes.length}개 표시', style: theme.textTheme.small, ), const Spacer(), if (controller.isLoading) const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), ], ), ), const SizedBox(height: 16), // 테이블 Expanded( child: ZipcodeTable( zipcodes: controller.zipcodes, currentPage: controller.currentPage, totalPages: controller.totalPages, onPageChanged: controller.goToPage, onSelect: (zipcode) { controller.selectZipcode(zipcode); if (widget.onSelect != null) { // 다이얼로그로 사용될 때 widget.onSelect!(zipcode); } else { // 일반 화면으로 사용될 때 _showSuccessToast('우편번호 ${zipcode.zipcode}를 선택했습니다'); } }, maxWidthCap: widget.tableMaxWidthFraction != null ? MediaQuery.sizeOf(context).width * widget.tableMaxWidthFraction! : null, ), ), ], ), ), ), ], ), ); }, ); } }