사용하지 않는 파일 정리 전 백업 (Phase 10 완료 후 상태)
This commit is contained in:
@@ -227,14 +227,12 @@ class UserFormController extends ChangeNotifier {
|
||||
(_) => onResult(null),
|
||||
);
|
||||
} else {
|
||||
// 사용자 생성
|
||||
// 사용자 생성 (백엔드 API v1 준수)
|
||||
final params = CreateUserParams(
|
||||
username: username,
|
||||
email: email,
|
||||
password: password,
|
||||
name: name,
|
||||
email: email.isEmpty ? null : email,
|
||||
phone: phoneNumber.isEmpty ? null : phoneNumber,
|
||||
role: role,
|
||||
companiesId: 1, // TODO: 실제 회사 선택 기능 필요
|
||||
);
|
||||
|
||||
final result = await _createUserUseCase(params);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/core/controllers/base_list_controller.dart';
|
||||
@@ -7,6 +6,8 @@ import 'package:superport/domain/usecases/user/get_users_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/create_user_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/check_username_availability_usecase.dart';
|
||||
import 'package:superport/domain/repositories/user_repository.dart';
|
||||
import 'package:superport/domain/repositories/company_repository.dart';
|
||||
import 'package:superport/models/company_item_model.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
|
||||
/// 사용자 목록 화면 컨트롤러 (서버 API v0.2.1 대응)
|
||||
@@ -16,6 +17,7 @@ class UserListController extends BaseListController<User> {
|
||||
late final CreateUserUseCase _createUserUseCase;
|
||||
late final CheckUsernameAvailabilityUseCase _checkUsernameUseCase;
|
||||
late final UserRepository _userRepository;
|
||||
late final CompanyRepository _companyRepository;
|
||||
|
||||
// 필터 옵션 (서버 API v0.2.1 지원 필드만)
|
||||
UserRole? _filterRole;
|
||||
@@ -31,12 +33,25 @@ class UserListController extends BaseListController<User> {
|
||||
// 사용자명 중복 체크 상태
|
||||
bool _usernameCheckInProgress = false;
|
||||
bool get isCheckingUsername => _usernameCheckInProgress;
|
||||
|
||||
// 회사 선택 관련 상태
|
||||
List<CompanyItem> _companies = [];
|
||||
int? _selectedCompanyId;
|
||||
bool _isLoadingCompanies = false;
|
||||
|
||||
List<CompanyItem> get companies => _companies;
|
||||
int? get selectedCompanyId => _selectedCompanyId;
|
||||
bool get isLoadingCompanies => _isLoadingCompanies;
|
||||
|
||||
UserListController() {
|
||||
_getUsersUseCase = GetIt.instance<GetUsersUseCase>();
|
||||
_createUserUseCase = GetIt.instance<CreateUserUseCase>();
|
||||
_checkUsernameUseCase = GetIt.instance<CheckUsernameAvailabilityUseCase>();
|
||||
_userRepository = GetIt.instance<UserRepository>();
|
||||
_companyRepository = GetIt.instance<CompanyRepository>();
|
||||
|
||||
// 초기화 시 회사 목록 로드
|
||||
loadCompanies();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -118,6 +133,42 @@ class UserListController extends BaseListController<User> {
|
||||
loadData(isRefresh: true);
|
||||
}
|
||||
|
||||
/// 회사 목록 로드
|
||||
Future<void> loadCompanies() async {
|
||||
_isLoadingCompanies = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final result = await _companyRepository.getCompanies();
|
||||
result.fold(
|
||||
(failure) {
|
||||
// 에러 처리는 로그만 남기고 계속 진행
|
||||
print('회사 목록 로드 실패: ${failure.message}');
|
||||
},
|
||||
(paginatedResponse) {
|
||||
_companies = paginatedResponse.items
|
||||
.map((company) => CompanyItem.headquarters(company))
|
||||
.toList();
|
||||
// 첫 번째 회사를 기본 선택
|
||||
if (_companies.isNotEmpty && _selectedCompanyId == null) {
|
||||
_selectedCompanyId = _companies.first.company.id;
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
print('회사 목록 로드 중 예외 발생: $e');
|
||||
} finally {
|
||||
_isLoadingCompanies = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 회사 선택
|
||||
void selectCompany(int? companyId) {
|
||||
_selectedCompanyId = companyId;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 사용자 생성
|
||||
Future<void> createUser({
|
||||
required String username,
|
||||
@@ -126,14 +177,19 @@ class UserListController extends BaseListController<User> {
|
||||
required String name,
|
||||
String? phone,
|
||||
required UserRole role,
|
||||
int? companiesId,
|
||||
}) async {
|
||||
final effectiveCompanyId = companiesId ?? _selectedCompanyId;
|
||||
|
||||
if (effectiveCompanyId == null) {
|
||||
throw Exception('회사를 선택해주세요.');
|
||||
}
|
||||
|
||||
final params = CreateUserParams(
|
||||
username: username,
|
||||
email: email,
|
||||
password: password,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: role,
|
||||
companiesId: effectiveCompanyId,
|
||||
);
|
||||
|
||||
final result = await _createUserUseCase(params);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport/utils/validators.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:superport/screens/user/controllers/user_form_controller.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/utils/formatters/korean_phone_formatter.dart';
|
||||
|
||||
// 사용자 등록/수정 화면 (UI만 담당, 상태/로직 분리)
|
||||
class UserFormScreen extends StatefulWidget {
|
||||
@@ -42,7 +42,7 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
title: Text(controller.isEditMode ? '사용자 수정' : '사용자 등록'),
|
||||
),
|
||||
body: controller.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
? const Center(child: ShadProgress())
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
@@ -89,9 +89,7 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
height: 20,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
child: ShadProgress(),
|
||||
),
|
||||
)
|
||||
: controller.isUsernameAvailable != null
|
||||
@@ -153,45 +151,52 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
],
|
||||
|
||||
// 수정 모드에서 비밀번호 변경 (선택사항)
|
||||
if (controller.isEditMode) ...[
|
||||
ExpansionTile(
|
||||
title: const Text('비밀번호 변경'),
|
||||
if (controller.isEditMode) ...[
|
||||
ShadAccordion<int>(
|
||||
children: [
|
||||
_buildPasswordField(
|
||||
label: '새 비밀번호',
|
||||
controller: _passwordController,
|
||||
hintText: '변경할 경우만 입력하세요',
|
||||
obscureText: !_showPassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_showPassword = !_showPassword;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty && value.length < 6) {
|
||||
return '비밀번호는 6자 이상이어야 합니다';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) => controller.password = value ?? '',
|
||||
),
|
||||
|
||||
_buildPasswordField(
|
||||
label: '새 비밀번호 확인',
|
||||
controller: _confirmPasswordController,
|
||||
hintText: '비밀번호를 다시 입력하세요',
|
||||
obscureText: !_showConfirmPassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_showConfirmPassword = !_showConfirmPassword;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (_passwordController.text.isNotEmpty && value != _passwordController.text) {
|
||||
return '비밀번호가 일치하지 않습니다';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
ShadAccordionItem(
|
||||
value: 1,
|
||||
title: const Text('비밀번호 변경'),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildPasswordField(
|
||||
label: '새 비밀번호',
|
||||
controller: _passwordController,
|
||||
hintText: '변경할 경우만 입력하세요',
|
||||
obscureText: !_showPassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_showPassword = !_showPassword;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty && value.length < 6) {
|
||||
return '비밀번호는 6자 이상이어야 합니다';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) => controller.password = value ?? '',
|
||||
),
|
||||
|
||||
_buildPasswordField(
|
||||
label: '새 비밀번호 확인',
|
||||
controller: _confirmPasswordController,
|
||||
hintText: '비밀번호를 다시 입력하세요',
|
||||
obscureText: !_showConfirmPassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_showConfirmPassword = !_showConfirmPassword;
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (_passwordController.text.isNotEmpty && value != _passwordController.text) {
|
||||
return '비밀번호가 일치하지 않습니다';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -221,54 +226,22 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
const SizedBox(height: 24),
|
||||
// 오류 메시지 표시
|
||||
if (controller.error != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade50,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.red.shade200),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.error_outline, color: Colors.red.shade700),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
controller.error!,
|
||||
style: TextStyle(color: Colors.red.shade700),
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: ShadAlert.destructive(
|
||||
title: const Text('오류'),
|
||||
description: Text(controller.error!),
|
||||
),
|
||||
),
|
||||
// 저장 버튼
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
child: ShadButton(
|
||||
onPressed: controller.isLoading
|
||||
? null
|
||||
: () => _onSaveUser(controller),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: ShadcnTheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: controller.isLoading
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
controller.isEditMode ? '수정하기' : '등록하기',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
size: ShadButtonSize.lg,
|
||||
child: Text(controller.isEditMode ? '수정하기' : '등록하기'),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -294,6 +267,7 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
void Function(String)? onChanged,
|
||||
Widget? suffixIcon,
|
||||
}) {
|
||||
final controller = TextEditingController(text: initialValue);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Column(
|
||||
@@ -301,12 +275,9 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
TextFormField(
|
||||
initialValue: initialValue,
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
suffixIcon: suffixIcon,
|
||||
),
|
||||
ShadInputFormField(
|
||||
controller: controller,
|
||||
placeholder: Text(hintText),
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
validator: validator,
|
||||
@@ -335,18 +306,10 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
TextFormField(
|
||||
ShadInputFormField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
),
|
||||
onPressed: onToggleVisibility,
|
||||
),
|
||||
),
|
||||
placeholder: Text(hintText),
|
||||
validator: validator,
|
||||
onSaved: onSaved,
|
||||
),
|
||||
@@ -355,7 +318,7 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// 전화번호 입력 섹션 (드롭다운 + 텍스트 필드)
|
||||
// 전화번호 입력 섹션 (통합 입력 필드)
|
||||
Widget _buildPhoneNumberSection(UserFormController controller) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
@@ -364,78 +327,28 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
children: [
|
||||
const Text('전화번호', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
// 접두사 드롭다운 (010, 02, 031 등)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: DropdownButton<String>(
|
||||
value: controller.phonePrefix,
|
||||
items: controller.phonePrefixes.map((prefix) {
|
||||
return DropdownMenuItem(
|
||||
value: prefix,
|
||||
child: Text(prefix),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
controller.updatePhonePrefix(value);
|
||||
}
|
||||
},
|
||||
underline: Container(), // 밑줄 제거
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text('-', style: TextStyle(fontSize: 16)),
|
||||
const SizedBox(width: 8),
|
||||
// 전화번호 입력 (7-8자리)
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
initialValue: controller.phoneNumber,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '1234567 또는 12345678',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(8),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (value.length < 7 || value.length > 8) {
|
||||
return '전화번호는 7-8자리 숫자를 입력해주세요';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (value) {
|
||||
controller.updatePhoneNumber(value);
|
||||
},
|
||||
onSaved: (value) {
|
||||
if (value != null) {
|
||||
controller.updatePhoneNumber(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
ShadInputFormField(
|
||||
controller: TextEditingController(text: controller.combinedPhoneNumber),
|
||||
placeholder: const Text('010-1234-5678'),
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
KoreanPhoneFormatter(), // 한국식 전화번호 자동 포맷팅
|
||||
],
|
||||
validator: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
return PhoneValidator.validate(value);
|
||||
}
|
||||
return null; // 선택 필드이므로 비어있어도 OK
|
||||
},
|
||||
onChanged: (value) {
|
||||
controller.updatePhoneNumber(value);
|
||||
},
|
||||
onSaved: (value) {
|
||||
if (value != null) {
|
||||
controller.updatePhoneNumber(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (controller.combinedPhoneNumber.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
'전화번호: ${controller.combinedPhoneNumber}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -450,14 +363,11 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
children: [
|
||||
const Text('권한 *', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
DropdownButtonFormField<UserRole>(
|
||||
value: controller.role,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '권한을 선택하세요',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: UserRole.values.map((role) {
|
||||
return DropdownMenuItem<UserRole>(
|
||||
ShadSelect<UserRole>(
|
||||
selectedOptionBuilder: (context, value) => Text(value.displayName ?? ''),
|
||||
placeholder: const Text('권한을 선택하세요'),
|
||||
options: UserRole.values.map((role) {
|
||||
return ShadOption(
|
||||
value: role,
|
||||
child: Text(role.displayName),
|
||||
);
|
||||
@@ -467,12 +377,6 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
controller.role = value;
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null) {
|
||||
return '권한을 선택해주세요';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
@@ -494,17 +398,19 @@ class _UserFormScreenState extends State<UserFormScreen> {
|
||||
void _onSaveUser(UserFormController controller) async {
|
||||
await controller.saveUser((error) {
|
||||
if (error != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(error),
|
||||
backgroundColor: Colors.red,
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast.destructive(
|
||||
title: const Text('오류'),
|
||||
description: Text(error),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(controller.isEditMode ? '사용자 정보가 수정되었습니다' : '사용자가 등록되었습니다'),
|
||||
backgroundColor: Colors.green,
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast(
|
||||
title: const Text('성공'),
|
||||
description: Text(
|
||||
controller.isEditMode ? '사용자 정보가 수정되었습니다' : '사용자가 등록되었습니다',
|
||||
),
|
||||
),
|
||||
);
|
||||
Navigator.pop(context, true);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport/models/user_model.dart';
|
||||
import 'package:superport/screens/common/theme_shadcn.dart';
|
||||
import 'package:superport/screens/common/components/shadcn_components.dart';
|
||||
import 'package:superport/screens/common/widgets/pagination.dart';
|
||||
import 'package:superport/screens/user/controllers/user_list_controller.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/utils/user_utils.dart';
|
||||
|
||||
/// shadcn/ui 스타일로 재설계된 사용자 관리 화면
|
||||
class UserList extends StatefulWidget {
|
||||
@@ -55,11 +54,6 @@ class _UserListState extends State<UserList> {
|
||||
});
|
||||
}
|
||||
|
||||
/// 회사명 반환 함수
|
||||
String _getCompanyName(int companyId) {
|
||||
// TODO: CompanyService를 통해 회사 정보 가져오기
|
||||
return '회사 $companyId'; // 임시 처리
|
||||
}
|
||||
|
||||
/// 상태별 색상 반환
|
||||
Color _getStatusColor(bool isActive) {
|
||||
@@ -112,26 +106,29 @@ class _UserListState extends State<UserList> {
|
||||
|
||||
/// 사용자 삭제 다이얼로그
|
||||
void _showDeleteDialog(int userId, String userName) {
|
||||
showDialog(
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
builder: (context) => ShadDialog(
|
||||
title: const Text('사용자 삭제'),
|
||||
content: Text('"$userName" 사용자를 정말로 삭제하시겠습니까?'),
|
||||
description: Text('"$userName" 사용자를 정말로 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
ShadButton.outline(
|
||||
child: const Text('취소'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
ShadButton.destructive(
|
||||
child: const Text('삭제'),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await _controller.deleteUser(userId);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('사용자가 삭제되었습니다')),
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast(
|
||||
title: const Text('삭제 완료'),
|
||||
description: const Text('사용자가 삭제되었습니다'),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('삭제', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -143,23 +140,23 @@ class _UserListState extends State<UserList> {
|
||||
final newStatus = !user.isActive;
|
||||
final statusText = newStatus ? '활성화' : '비활성화';
|
||||
|
||||
showDialog(
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('사용자 상태 변경'),
|
||||
content: Text('"${user.name}" 사용자를 $statusText 하시겠습니까?'),
|
||||
builder: (context) => ShadDialog(
|
||||
title: const Text('사용자 상태 변경'),
|
||||
description: Text('"${user.name}" 사용자를 $statusText 하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
ShadButton.outline(
|
||||
child: const Text('취소'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
ShadButton(
|
||||
child: Text(statusText),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await _controller.changeUserStatus(user, newStatus);
|
||||
},
|
||||
child: Text(statusText),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -173,7 +170,7 @@ class _UserListState extends State<UserList> {
|
||||
builder: (context, child) {
|
||||
if (_controller.isLoading && _controller.users.isEmpty) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: ShadProgress(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -226,28 +223,9 @@ class _UserListState extends State<UserList> {
|
||||
child: Column(
|
||||
children: [
|
||||
// 검색 바
|
||||
TextField(
|
||||
ShadInputFormField(
|
||||
controller: _searchController,
|
||||
decoration: InputDecoration(
|
||||
hintText: '이름, 이메일, 사용자명으로 검색...',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
_searchController.clear();
|
||||
_controller.setSearchQuery('');
|
||||
},
|
||||
)
|
||||
: null,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: ShadcnTheme.spacing4,
|
||||
vertical: ShadcnTheme.spacing3,
|
||||
),
|
||||
),
|
||||
placeholder: const Text('이름, 이메일, 사용자명으로 검색...'),
|
||||
),
|
||||
const SizedBox(height: ShadcnTheme.spacing3),
|
||||
// 필터 버튼들
|
||||
@@ -309,12 +287,13 @@ class _UserListState extends State<UserList> {
|
||||
// 관리자용 비활성 포함 체크박스
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
ShadCheckbox(
|
||||
value: _controller.includeInactive,
|
||||
onChanged: (_) => setState(() {
|
||||
_controller.toggleIncludeInactive();
|
||||
}),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text('비활성 포함'),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user