사용하지 않는 파일 정리 전 백업 (Phase 10 완료 후 상태)
This commit is contained in:
596
lib/screens/administrator/administrator_list.dart
Normal file
596
lib/screens/administrator/administrator_list.dart
Normal file
@@ -0,0 +1,596 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport/data/models/administrator_dto.dart';
|
||||
import 'package:superport/screens/administrator/controllers/administrator_controller.dart';
|
||||
import 'package:superport/screens/common/widgets/pagination.dart';
|
||||
|
||||
/// 관리자 목록 화면 (shadcn/ui 스타일)
|
||||
class AdministratorList extends StatefulWidget {
|
||||
const AdministratorList({super.key});
|
||||
|
||||
@override
|
||||
State<AdministratorList> createState() => _AdministratorListState();
|
||||
}
|
||||
|
||||
class _AdministratorListState extends State<AdministratorList> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
late AdministratorController _controller;
|
||||
Timer? _debounce;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = Provider.of<AdministratorController>(context, listen: false);
|
||||
|
||||
// 초기 데이터 로드
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_controller.initialize();
|
||||
});
|
||||
|
||||
// 검색 디바운싱
|
||||
_searchController.addListener(() {
|
||||
_onSearchChanged(_searchController.text);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_debounce?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// 검색어 변경 처리 (디바운싱)
|
||||
void _onSearchChanged(String query) {
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 300), () {
|
||||
_controller.setSearchQuery(query);
|
||||
});
|
||||
}
|
||||
|
||||
/// 관리자 추가 다이얼로그
|
||||
void _showAddDialog() {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => _AdministratorFormDialog(
|
||||
title: '관리자 추가',
|
||||
onSubmit: (name, phone, mobile, email, password) async {
|
||||
final success = await _controller.createAdministrator(
|
||||
name: name,
|
||||
phone: phone,
|
||||
mobile: mobile,
|
||||
email: email,
|
||||
password: password ?? '',
|
||||
);
|
||||
|
||||
if (success && mounted) {
|
||||
Navigator.of(context).pop();
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('생성 완료'),
|
||||
description: Text('관리자가 생성되었습니다'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return success;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 관리자 수정 다이얼로그
|
||||
void _showEditDialog(AdministratorDto administrator) {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => _AdministratorFormDialog(
|
||||
title: '관리자 수정',
|
||||
administrator: administrator,
|
||||
onSubmit: (name, phone, mobile, email, password) async {
|
||||
final success = await _controller.updateAdministrator(
|
||||
administrator.id!,
|
||||
name: name,
|
||||
phone: phone,
|
||||
mobile: mobile,
|
||||
email: email,
|
||||
password: password ?? '',
|
||||
);
|
||||
|
||||
if (success && mounted) {
|
||||
Navigator.of(context).pop();
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('수정 완료'),
|
||||
description: Text('관리자 정보가 수정되었습니다'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return success;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 관리자 삭제 확인 다이얼로그
|
||||
void _showDeleteDialog(AdministratorDto administrator) {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => ShadDialog(
|
||||
title: const Text('관리자 삭제'),
|
||||
description: Text('"${administrator.name}" 관리자를 정말로 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
ShadButton.outline(
|
||||
child: const Text('취소'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
ShadButton.destructive(
|
||||
child: const Text('삭제'),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final success = await _controller.deleteAdministrator(administrator.id!);
|
||||
|
||||
if (success && mounted) {
|
||||
ShadToaster.of(context).show(
|
||||
const ShadToast(
|
||||
title: Text('삭제 완료'),
|
||||
description: Text('관리자가 삭제되었습니다'),
|
||||
),
|
||||
);
|
||||
} else if (mounted && _controller.errorMessage != null) {
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast.destructive(
|
||||
title: const Text('삭제 실패'),
|
||||
description: Text(_controller.errorMessage!),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 데이터 테이블 컬럼 정의
|
||||
List<DataColumn> get _columns => [
|
||||
const DataColumn(label: Text('이름')),
|
||||
const DataColumn(label: Text('이메일')),
|
||||
const DataColumn(label: Text('전화번호')),
|
||||
const DataColumn(label: Text('휴대폰')),
|
||||
const DataColumn(label: Text('작업')),
|
||||
];
|
||||
|
||||
/// 데이터 테이블 행 생성
|
||||
List<DataRow> _buildRows(List<AdministratorDto> administrators) {
|
||||
return administrators.map((admin) {
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(Text(admin.name)),
|
||||
DataCell(Text(admin.email)),
|
||||
DataCell(Text(admin.phone)),
|
||||
DataCell(Text(admin.mobile)),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _showEditDialog(admin),
|
||||
child: const Icon(Icons.edit, size: 16),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () => _showDeleteDialog(admin),
|
||||
child: const Icon(Icons.delete, size: 16, color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
// Action Bar
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
// 제목
|
||||
const Text(
|
||||
'관리자 관리',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// 검색창
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Icon(Icons.search, size: 16, color: Colors.grey),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '이름 또는 이메일로 검색',
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// 추가 버튼
|
||||
ShadButton(
|
||||
onPressed: _showAddDialog,
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.add, size: 16),
|
||||
SizedBox(width: 4),
|
||||
Text('관리자 추가'),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// 새로고침 버튼
|
||||
ShadButton.outline(
|
||||
onPressed: () => _controller.refresh(),
|
||||
child: const Icon(Icons.refresh, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Content
|
||||
Expanded(
|
||||
child: Consumer<AdministratorController>(
|
||||
builder: (context, controller, child) {
|
||||
if (controller.isLoading && controller.administrators.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (controller.errorMessage != null && controller.administrators.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error_outline, size: 64, color: Colors.red),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
controller.errorMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ShadButton(
|
||||
child: const Text('다시 시도'),
|
||||
onPressed: () => controller.refresh(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Summary Card
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: ShadCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.admin_panel_settings, size: 32),
|
||||
const SizedBox(width: 16),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'전체 관리자',
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
Text(
|
||||
'${controller.totalCount}명',
|
||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
if (controller.isLoading)
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Data Table
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: ShadCard(
|
||||
child: controller.administrators.isEmpty
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(64),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(Icons.inbox, size: 64, color: Colors.grey),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
'관리자가 없습니다',
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: DataTable(
|
||||
columns: _columns,
|
||||
rows: _buildRows(controller.administrators),
|
||||
columnSpacing: 24,
|
||||
headingRowHeight: 56,
|
||||
dataRowMinHeight: 48,
|
||||
dataRowMaxHeight: 56,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Pagination
|
||||
if (controller.totalPages > 1)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Pagination(
|
||||
totalCount: controller.totalCount,
|
||||
currentPage: controller.currentPage,
|
||||
pageSize: controller.pageSize,
|
||||
onPageChanged: (page) => controller.goToPage(page),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 관리자 추가/수정 다이얼로그
|
||||
class _AdministratorFormDialog extends StatefulWidget {
|
||||
final String title;
|
||||
final AdministratorDto? administrator;
|
||||
final Future<bool> Function(String name, String phone, String mobile, String email, String? password) onSubmit;
|
||||
|
||||
const _AdministratorFormDialog({
|
||||
required this.title,
|
||||
this.administrator,
|
||||
required this.onSubmit,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_AdministratorFormDialog> createState() => _AdministratorFormDialogState();
|
||||
}
|
||||
|
||||
class _AdministratorFormDialogState extends State<_AdministratorFormDialog> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late final TextEditingController _nameController;
|
||||
late final TextEditingController _phoneController;
|
||||
late final TextEditingController _mobileController;
|
||||
late final TextEditingController _emailController;
|
||||
late final TextEditingController _passwordController;
|
||||
|
||||
bool _isSubmitting = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController = TextEditingController(text: widget.administrator?.name ?? '');
|
||||
_phoneController = TextEditingController(text: widget.administrator?.phone ?? '');
|
||||
_mobileController = TextEditingController(text: widget.administrator?.mobile ?? '');
|
||||
_emailController = TextEditingController(text: widget.administrator?.email ?? '');
|
||||
_passwordController = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_mobileController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool get _isEditMode => widget.administrator != null;
|
||||
|
||||
Future<void> _handleSubmit() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final success = await widget.onSubmit(
|
||||
_nameController.text.trim(),
|
||||
_phoneController.text.trim(),
|
||||
_mobileController.text.trim(),
|
||||
_emailController.text.trim(),
|
||||
_passwordController.text.isNotEmpty ? _passwordController.text : null,
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
// 에러는 Controller에서 처리되므로 여기서는 로딩 상태만 해제
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShadDialog(
|
||||
title: Text(widget.title),
|
||||
child: SizedBox(
|
||||
width: 500,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 이름
|
||||
ShadInputFormField(
|
||||
controller: _nameController,
|
||||
label: const Text('이름'),
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return '이름을 입력해주세요';
|
||||
}
|
||||
if (value.length > 100) {
|
||||
return '이름은 100자 이내로 입력해주세요';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 이메일
|
||||
ShadInputFormField(
|
||||
controller: _emailController,
|
||||
label: const Text('이메일'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return '이메일을 입력해주세요';
|
||||
}
|
||||
if (!RegExp(r'^[^\s@]+@[^\s@]+\.[^\s@]+$').hasMatch(value.trim())) {
|
||||
return '유효한 이메일을 입력해주세요';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 전화번호
|
||||
ShadInputFormField(
|
||||
controller: _phoneController,
|
||||
label: const Text('전화번호'),
|
||||
keyboardType: TextInputType.phone,
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return '전화번호를 입력해주세요';
|
||||
}
|
||||
final cleaned = value.replaceAll(RegExp(r'[\s\-\(\)]'), '');
|
||||
if (!RegExp(r'^\d{8,11}$').hasMatch(cleaned)) {
|
||||
return '유효한 전화번호를 입력해주세요 (8-11자리 숫자)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 휴대폰
|
||||
ShadInputFormField(
|
||||
controller: _mobileController,
|
||||
label: const Text('휴대폰'),
|
||||
keyboardType: TextInputType.phone,
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return '휴대폰 번호를 입력해주세요';
|
||||
}
|
||||
final cleaned = value.replaceAll(RegExp(r'[\s\-\(\)]'), '');
|
||||
if (!RegExp(r'^\d{8,11}$').hasMatch(cleaned)) {
|
||||
return '유효한 휴대폰 번호를 입력해주세요 (8-11자리 숫자)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 비밀번호
|
||||
ShadInputFormField(
|
||||
controller: _passwordController,
|
||||
label: Text(_isEditMode ? '새 비밀번호 (선택사항)' : '비밀번호'),
|
||||
obscureText: true,
|
||||
validator: (value) {
|
||||
// 수정 모드에서는 비밀번호 선택사항
|
||||
if (_isEditMode && (value == null || value.isEmpty)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 생성 모드에서는 비밀번호 필수
|
||||
if (!_isEditMode && (value == null || value.trim().isEmpty)) {
|
||||
return '비밀번호를 입력해주세요';
|
||||
}
|
||||
|
||||
// 입력된 경우 길이 검증
|
||||
if (value != null && value.isNotEmpty && value.length < 6) {
|
||||
return '비밀번호는 최소 6자 이상이어야 합니다';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
ShadButton.outline(
|
||||
child: const Text('취소'),
|
||||
onPressed: _isSubmitting ? null : () => Navigator.of(context).pop(),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: _isSubmitting ? null : _handleSubmit,
|
||||
child: _isSubmitting
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: Text(_isEditMode ? '수정' : '생성'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/data/models/administrator_dto.dart';
|
||||
import 'package:superport/domain/usecases/administrator_usecase.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
|
||||
/// 관리자 화면 컨트롤러 (Provider 패턴)
|
||||
/// 관리자 목록 조회, CRUD, 검색 등의 상태 관리
|
||||
@injectable
|
||||
class AdministratorController extends ChangeNotifier {
|
||||
final AdministratorUseCase _administratorUseCase;
|
||||
|
||||
AdministratorController(this._administratorUseCase);
|
||||
|
||||
// 상태 변수들
|
||||
List<AdministratorDto> _administrators = [];
|
||||
AdministratorDto? _selectedAdministrator;
|
||||
bool _isLoading = false;
|
||||
String? _errorMessage;
|
||||
|
||||
// 페이지네이션
|
||||
int _currentPage = 1;
|
||||
int _totalPages = 1;
|
||||
int _totalCount = 0;
|
||||
final int _pageSize = PaginationConstants.defaultPageSize;
|
||||
|
||||
// 검색
|
||||
String _searchQuery = '';
|
||||
|
||||
// Form 관련 상태
|
||||
bool _isFormSubmitting = false;
|
||||
String? _formErrorMessage;
|
||||
|
||||
// Getters
|
||||
List<AdministratorDto> get administrators => _administrators;
|
||||
AdministratorDto? get selectedAdministrator => _selectedAdministrator;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get errorMessage => _errorMessage;
|
||||
int get currentPage => _currentPage;
|
||||
int get totalPages => _totalPages;
|
||||
int get totalCount => _totalCount;
|
||||
int get pageSize => _pageSize;
|
||||
String get searchQuery => _searchQuery;
|
||||
bool get hasNextPage => _currentPage < _totalPages;
|
||||
bool get hasPreviousPage => _currentPage > 1;
|
||||
|
||||
// Form 관련 Getters
|
||||
bool get isFormSubmitting => _isFormSubmitting;
|
||||
String? get formErrorMessage => _formErrorMessage;
|
||||
|
||||
/// 컨트롤러 초기화
|
||||
Future<void> initialize() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
await loadAdministrators();
|
||||
}
|
||||
|
||||
/// 관리자 목록 로드
|
||||
Future<void> loadAdministrators({bool refresh = false}) async {
|
||||
if (refresh) {
|
||||
_currentPage = 1;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final result = await _administratorUseCase.getAdministrators(
|
||||
page: _currentPage,
|
||||
pageSize: _pageSize,
|
||||
search: _searchQuery.isNotEmpty ? _searchQuery : null,
|
||||
);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_setError('관리자 목록을 불러오는데 실패했습니다: ${failure.message}');
|
||||
},
|
||||
(response) {
|
||||
_administrators = List.from(response.items);
|
||||
_totalCount = response.totalCount;
|
||||
_totalPages = response.totalPages;
|
||||
_currentPage = response.currentPage;
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_setError('관리자 목록을 불러오는데 실패했습니다: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 관리자 선택/조회
|
||||
Future<void> selectAdministrator(int id) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final result = await _administratorUseCase.getAdministratorById(id);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_setError('관리자 정보를 불러오는데 실패했습니다: ${failure.message}');
|
||||
},
|
||||
(administrator) {
|
||||
_selectedAdministrator = administrator;
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_setError('관리자 정보를 불러오는데 실패했습니다: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 관리자 생성
|
||||
Future<bool> createAdministrator({
|
||||
required String name,
|
||||
required String phone,
|
||||
required String mobile,
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
_setFormSubmitting(true);
|
||||
_clearFormError();
|
||||
|
||||
try {
|
||||
final request = AdministratorRequestDto(
|
||||
name: name.trim(),
|
||||
phone: phone.trim(),
|
||||
mobile: mobile.trim(),
|
||||
email: email.trim(),
|
||||
passwd: password,
|
||||
);
|
||||
|
||||
final result = await _administratorUseCase.createAdministrator(request);
|
||||
|
||||
return result.fold(
|
||||
(failure) {
|
||||
_setFormError('관리자 생성에 실패했습니다: ${failure.message}');
|
||||
return false;
|
||||
},
|
||||
(administrator) {
|
||||
// 목록 새로고침
|
||||
loadAdministrators(refresh: true);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_setFormError('관리자 생성에 실패했습니다: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setFormSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 관리자 정보 수정
|
||||
Future<bool> updateAdministrator(
|
||||
int id, {
|
||||
String? name,
|
||||
String? phone,
|
||||
String? mobile,
|
||||
String? email,
|
||||
String? password,
|
||||
}) async {
|
||||
_setFormSubmitting(true);
|
||||
_clearFormError();
|
||||
|
||||
try {
|
||||
final request = AdministratorUpdateRequestDto(
|
||||
name: name?.trim(),
|
||||
phone: phone?.trim(),
|
||||
mobile: mobile?.trim(),
|
||||
email: email?.trim(),
|
||||
passwd: password,
|
||||
);
|
||||
|
||||
final result = await _administratorUseCase.updateAdministrator(id, request);
|
||||
|
||||
return result.fold(
|
||||
(failure) {
|
||||
_setFormError('관리자 정보 수정에 실패했습니다: ${failure.message}');
|
||||
return false;
|
||||
},
|
||||
(administrator) {
|
||||
// 목록 새로고침
|
||||
loadAdministrators(refresh: true);
|
||||
// 선택된 관리자 업데이트
|
||||
if (_selectedAdministrator?.id == id) {
|
||||
_selectedAdministrator = administrator;
|
||||
}
|
||||
notifyListeners();
|
||||
return true;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_setFormError('관리자 정보 수정에 실패했습니다: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setFormSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 관리자 삭제
|
||||
Future<bool> deleteAdministrator(int id) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final result = await _administratorUseCase.deleteAdministrator(id);
|
||||
|
||||
return result.fold(
|
||||
(failure) {
|
||||
_setError('관리자 삭제에 실패했습니다: ${failure.message}');
|
||||
return false;
|
||||
},
|
||||
(_) {
|
||||
// 목록에서 제거
|
||||
_administrators.removeWhere((admin) => admin.id == id);
|
||||
// 선택된 관리자가 삭제된 경우 클리어
|
||||
if (_selectedAdministrator?.id == id) {
|
||||
_selectedAdministrator = null;
|
||||
}
|
||||
// 총 개수 업데이트
|
||||
_totalCount--;
|
||||
notifyListeners();
|
||||
return true;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_setError('관리자 삭제에 실패했습니다: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 검색 쿼리 설정
|
||||
void setSearchQuery(String query) {
|
||||
_searchQuery = query.trim();
|
||||
loadAdministrators(refresh: true);
|
||||
}
|
||||
|
||||
/// 검색 초기화
|
||||
void clearSearch() {
|
||||
if (_searchQuery.isNotEmpty) {
|
||||
_searchQuery = '';
|
||||
loadAdministrators(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 다음 페이지 로드
|
||||
Future<void> loadNextPage() async {
|
||||
if (hasNextPage && !_isLoading) {
|
||||
_currentPage++;
|
||||
await loadAdministrators();
|
||||
}
|
||||
}
|
||||
|
||||
/// 이전 페이지 로드
|
||||
Future<void> loadPreviousPage() async {
|
||||
if (hasPreviousPage && !_isLoading) {
|
||||
_currentPage--;
|
||||
await loadAdministrators();
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 페이지로 이동
|
||||
Future<void> goToPage(int page) async {
|
||||
if (page > 0 && page <= _totalPages && page != _currentPage && !_isLoading) {
|
||||
_currentPage = page;
|
||||
await loadAdministrators();
|
||||
}
|
||||
}
|
||||
|
||||
/// 이메일 중복 확인
|
||||
Future<bool> checkEmailDuplicate(String email, {int? excludeId}) async {
|
||||
try {
|
||||
final result = await _administratorUseCase.checkEmailDuplicate(
|
||||
email.trim(),
|
||||
excludeId: excludeId,
|
||||
);
|
||||
|
||||
return result.fold(
|
||||
(failure) => true, // 에러 시 안전하게 중복으로 처리
|
||||
(isDuplicate) => isDuplicate,
|
||||
);
|
||||
} catch (e) {
|
||||
return true; // 에러 시 안전하게 중복으로 처리
|
||||
}
|
||||
}
|
||||
|
||||
/// 선택된 관리자 클리어
|
||||
void clearSelectedAdministrator() {
|
||||
_selectedAdministrator = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 목록 새로고침
|
||||
Future<void> refresh() async {
|
||||
await loadAdministrators(refresh: true);
|
||||
}
|
||||
|
||||
// 내부 상태 관리 메서드들
|
||||
void _setLoading(bool loading) {
|
||||
_isLoading = loading;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _setError(String error) {
|
||||
_errorMessage = error;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _clearError() {
|
||||
_errorMessage = null;
|
||||
}
|
||||
|
||||
void _setFormSubmitting(bool submitting) {
|
||||
_isFormSubmitting = submitting;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _setFormError(String error) {
|
||||
_formErrorMessage = error;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _clearFormError() {
|
||||
_formErrorMessage = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// 추가적인 정리 작업이 필요한 경우 여기서 수행
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user