forked from baron/baron-sso
252 lines
8.1 KiB
Dart
252 lines
8.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import '../../domain/notifiers/profile_notifier.dart';
|
|
|
|
class EditProfilePage extends ConsumerStatefulWidget {
|
|
const EditProfilePage({super.key});
|
|
|
|
@override
|
|
ConsumerState<EditProfilePage> createState() => _EditProfilePageState();
|
|
}
|
|
|
|
class _EditProfilePageState extends ConsumerState<EditProfilePage> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
late TextEditingController _nameController;
|
|
late TextEditingController _phoneController;
|
|
late TextEditingController _codeController;
|
|
late TextEditingController _departmentController;
|
|
|
|
String? _initialPhone;
|
|
bool _isPhoneChanged = false;
|
|
bool _isPhoneVerified = false;
|
|
bool _isCodeSent = false;
|
|
bool _isVerifying = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
final profile = ref.read(profileProvider).value;
|
|
_initialPhone = profile?.phone ?? '';
|
|
_nameController = TextEditingController(text: profile?.name ?? '');
|
|
_phoneController = TextEditingController(text: _initialPhone);
|
|
_codeController = TextEditingController();
|
|
_departmentController = TextEditingController(text: profile?.department ?? '');
|
|
|
|
_phoneController.addListener(() {
|
|
setState(() {
|
|
_isPhoneChanged = _phoneController.text != _initialPhone;
|
|
if (_isPhoneChanged) {
|
|
_isPhoneVerified = false;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameController.dispose();
|
|
_phoneController.dispose();
|
|
_codeController.dispose();
|
|
_departmentController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _sendCode() async {
|
|
final phone = _phoneController.text;
|
|
if (phone.isEmpty) return;
|
|
|
|
setState(() => _isVerifying = true);
|
|
try {
|
|
await ref.read(profileRepositoryProvider).sendUpdateCode(phone);
|
|
setState(() {
|
|
_isCodeSent = true;
|
|
_isVerifying = false;
|
|
});
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('인증번호가 전송되었습니다.')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
setState(() => _isVerifying = false);
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('전송 실패: $e')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _verifyCode() async {
|
|
final phone = _phoneController.text;
|
|
final code = _codeController.text;
|
|
if (code.isEmpty) return;
|
|
|
|
setState(() => _isVerifying = true);
|
|
try {
|
|
await ref.read(profileRepositoryProvider).verifyUpdateCode(phone, code);
|
|
setState(() {
|
|
_isPhoneVerified = true;
|
|
_isVerifying = false;
|
|
});
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('인증되었습니다.')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
setState(() => _isVerifying = false);
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('인증 실패: $e')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _save() async {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
if (_isPhoneChanged && !_isPhoneVerified) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('휴대폰 번호 인증이 필요합니다.')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await ref.read(profileProvider.notifier).updateProfile(
|
|
name: _nameController.text,
|
|
phone: _phoneController.text,
|
|
department: _departmentController.text,
|
|
);
|
|
if (mounted) {
|
|
context.pop();
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('정보가 수정되었습니다.')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('수정 실패: $e')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final profileState = ref.watch(profileProvider);
|
|
final isUpdating = profileState.isLoading;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('내 정보 수정'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: (isUpdating || (_isPhoneChanged && !_isPhoneVerified)) ? null : _save,
|
|
child: const Text('저장'),
|
|
),
|
|
],
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: ListView(
|
|
children: [
|
|
TextFormField(
|
|
controller: _nameController,
|
|
decoration: const InputDecoration(
|
|
labelText: '이름',
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
),
|
|
validator: (value) => (value == null || value.isEmpty) ? '이름을 입력해주세요.' : null,
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Phone Number Field
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: _phoneController,
|
|
decoration: InputDecoration(
|
|
labelText: '휴대폰 번호',
|
|
hintText: '01012345678',
|
|
prefixIcon: const Icon(Icons.phone_android),
|
|
suffixIcon: _isPhoneVerified
|
|
? const Icon(Icons.check_circle, color: Colors.green)
|
|
: null,
|
|
),
|
|
keyboardType: TextInputType.phone,
|
|
enabled: !_isPhoneVerified,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
if (_isPhoneChanged && !_isPhoneVerified)
|
|
ElevatedButton(
|
|
onPressed: _isVerifying ? null : _sendCode,
|
|
child: Text(_isCodeSent ? '재전송' : '인증요청'),
|
|
),
|
|
],
|
|
),
|
|
|
|
// OTP Code Field
|
|
if (_isCodeSent && !_isPhoneVerified) ...[
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: _codeController,
|
|
decoration: const InputDecoration(
|
|
labelText: '인증번호',
|
|
hintText: '6자리 입력',
|
|
prefixIcon: Icon(Icons.security),
|
|
),
|
|
keyboardType: TextInputType.number,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
ElevatedButton(
|
|
onPressed: _isVerifying ? null : _verifyCode,
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue[700], foregroundColor: Colors.white),
|
|
child: const Text('확인'),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
|
|
if (_isPhoneChanged && !_isPhoneVerified)
|
|
const Padding(
|
|
padding: EdgeInsets.only(top: 8.0, left: 4.0),
|
|
child: Text(
|
|
'휴대폰 번호를 변경하려면 SMS 인증이 필요합니다.',
|
|
style: TextStyle(color: Colors.orange, fontSize: 12),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 24),
|
|
TextFormField(
|
|
controller: _departmentController,
|
|
decoration: const InputDecoration(
|
|
labelText: '소속 (부서)',
|
|
prefixIcon: Icon(Icons.business),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 40),
|
|
if (isUpdating || _isVerifying)
|
|
const Center(child: CircularProgressIndicator()),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|