forked from baron/baron-sso
i18n 대대적 변경
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
import '../../../../core/notifiers/auth_notifier.dart';
|
||||
import '../../../../core/services/auth_token_store.dart';
|
||||
import '../../../../core/ui/layout_breakpoints.dart';
|
||||
@@ -229,14 +230,29 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
});
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('인증번호가 전송되었습니다.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone.code_sent',
|
||||
fallback: '인증번호가 전송되었습니다.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() => _isVerifying = false);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('전송 실패: $e')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone.send_failed',
|
||||
fallback: '전송 실패: {{error}}',
|
||||
params: {'error': e.toString()},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -256,7 +272,14 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
});
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('인증되었습니다.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone.verified',
|
||||
fallback: '인증되었습니다.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_editingField == 'phone') {
|
||||
@@ -266,7 +289,15 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
setState(() => _isVerifying = false);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('인증 실패: $e')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone.verify_failed',
|
||||
fallback: '인증 실패: {{error}}',
|
||||
params: {'error': e.toString()},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -279,15 +310,24 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
final confirmPassword = _confirmPasswordController?.text.trim() ?? '';
|
||||
|
||||
if (currentPassword.isEmpty) {
|
||||
setState(() => _passwordError = '현재 비밀번호를 입력해 주세요.');
|
||||
setState(() => _passwordError = tr(
|
||||
'msg.userfront.profile.password.current_required',
|
||||
fallback: '현재 비밀번호를 입력해 주세요.',
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (newPassword.isEmpty) {
|
||||
setState(() => _passwordError = '새 비밀번호를 입력해 주세요.');
|
||||
setState(() => _passwordError = tr(
|
||||
'msg.userfront.profile.password.new_required',
|
||||
fallback: '새 비밀번호를 입력해 주세요.',
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (newPassword != confirmPassword) {
|
||||
setState(() => _passwordError = '새 비밀번호가 일치하지 않습니다.');
|
||||
setState(() => _passwordError = tr(
|
||||
'msg.userfront.profile.password.mismatch',
|
||||
fallback: '새 비밀번호가 일치하지 않습니다.',
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,12 +346,19 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_newPasswordController?.clear();
|
||||
_confirmPasswordController?.clear();
|
||||
setState(() {
|
||||
_passwordSuccess = '비밀번호가 변경되었습니다.';
|
||||
_passwordSuccess = tr(
|
||||
'msg.userfront.profile.password.changed',
|
||||
fallback: '비밀번호가 변경되었습니다.',
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
final message = e.toString().replaceFirst('Exception: ', '');
|
||||
setState(() {
|
||||
_passwordError = '비밀번호 변경 실패: $message';
|
||||
_passwordError = tr(
|
||||
'msg.userfront.profile.password.change_failed',
|
||||
fallback: '비밀번호 변경 실패: {{error}}',
|
||||
params: {'error': message},
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
if (mounted) {
|
||||
@@ -385,26 +432,54 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
|
||||
if (_editingField == 'name' && nextName.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('이름을 입력해주세요.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.name_required',
|
||||
fallback: '이름을 입력해주세요.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_editingField == 'department' && nextDepartment.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('소속을 입력해주세요.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.department_required',
|
||||
fallback: '소속을 입력해주세요.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_editingField == 'phone') {
|
||||
if (nextPhone.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('휴대폰 번호를 입력해주세요.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone_required',
|
||||
fallback: '휴대폰 번호를 입력해주세요.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_isPhoneChanged && !_isPhoneVerified) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('휴대폰 번호 인증이 필요합니다.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.phone_verify_required',
|
||||
fallback: '휴대폰 번호 인증이 필요합니다.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -441,13 +516,28 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_departmentTouched = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('정보가 수정되었습니다.')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.update_success',
|
||||
fallback: '정보가 수정되었습니다.',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('수정 실패: $e')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
tr(
|
||||
'msg.userfront.profile.update_failed',
|
||||
fallback: '수정 실패: {{error}}',
|
||||
params: {'error': e.toString()},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -461,24 +551,32 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.home_outlined),
|
||||
title: const Text('대시보드'),
|
||||
title: Text(
|
||||
tr('ui.userfront.nav.dashboard', fallback: '대시보드'),
|
||||
),
|
||||
onTap: () => context.go('/'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person_outline),
|
||||
title: const Text('내 정보'),
|
||||
title: Text(
|
||||
tr('ui.userfront.nav.profile', fallback: '내 정보'),
|
||||
),
|
||||
selected: true,
|
||||
onTap: () => context.go('/profile'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.qr_code_scanner),
|
||||
title: const Text('QR 스캔'),
|
||||
title: Text(
|
||||
tr('ui.userfront.nav.qr_scan', fallback: 'QR 스캔'),
|
||||
),
|
||||
onTap: () => context.go('/scan'),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout),
|
||||
title: const Text('로그아웃'),
|
||||
title: Text(
|
||||
tr('ui.userfront.nav.logout', fallback: '로그아웃'),
|
||||
),
|
||||
onTap: _logout,
|
||||
),
|
||||
],
|
||||
@@ -525,9 +623,15 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
}
|
||||
|
||||
Widget _buildHeaderCard(UserProfile profile) {
|
||||
final name = profile.name.isEmpty ? '이름 없음' : profile.name;
|
||||
final email = profile.email.isEmpty ? '이메일 없음' : profile.email;
|
||||
final department = profile.department.isEmpty ? '소속 정보 없음' : profile.department;
|
||||
final name = profile.name.isEmpty
|
||||
? tr('msg.userfront.profile.name_missing', fallback: '이름 없음')
|
||||
: profile.name;
|
||||
final email = profile.email.isEmpty
|
||||
? tr('msg.userfront.profile.email_missing', fallback: '이메일 없음')
|
||||
: profile.email;
|
||||
final department = profile.department.isEmpty
|
||||
? tr('msg.userfront.profile.department_missing', fallback: '소속 정보 없음')
|
||||
: profile.department;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
@@ -538,7 +642,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
border: Border.all(color: _border),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.04),
|
||||
color: Colors.black.withValues(alpha: 10),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
@@ -556,8 +660,16 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'안녕하세요, $name님',
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: _ink),
|
||||
tr(
|
||||
'msg.userfront.profile.greeting',
|
||||
fallback: '안녕하세요, {{name}}님',
|
||||
params: {'name': name},
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: _ink,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(email, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
||||
@@ -566,7 +678,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_buildInfoChip(Icons.badge_outlined, '프로필 관리'),
|
||||
_buildInfoChip(
|
||||
Icons.badge_outlined,
|
||||
tr('ui.userfront.profile.manage', fallback: '프로필 관리'),
|
||||
),
|
||||
_buildInfoChip(Icons.apartment, profile.tenant?.name ?? department),
|
||||
],
|
||||
),
|
||||
@@ -588,7 +703,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
border: Border.all(color: _border),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.03),
|
||||
color: Colors.black.withValues(alpha: 8),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 6),
|
||||
),
|
||||
@@ -605,7 +720,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
title: Text(label),
|
||||
subtitle: Text(displayValue),
|
||||
trailing: Text(
|
||||
'읽기 전용',
|
||||
tr('ui.common.read_only', fallback: '읽기 전용'),
|
||||
style: TextStyle(color: Colors.grey[500], fontSize: 12),
|
||||
),
|
||||
);
|
||||
@@ -629,7 +744,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
subtitle: Text(displayValue),
|
||||
trailing: TextButton(
|
||||
onPressed: isUpdating ? null : () => _startEditing(field, profile),
|
||||
child: const Text('수정'),
|
||||
child: Text(tr('ui.common.edit', fallback: '수정')),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -657,7 +772,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
const SizedBox(width: 12),
|
||||
OutlinedButton(
|
||||
onPressed: isUpdating ? null : () => _cancelEditing(profile),
|
||||
child: const Text('취소'),
|
||||
child: Text(tr('ui.common.cancel', fallback: '취소')),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -672,11 +787,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
if (!isEditing) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('전화번호'),
|
||||
title: Text(
|
||||
tr('ui.userfront.profile.phone.title', fallback: '전화번호'),
|
||||
),
|
||||
subtitle: Text(displayValue),
|
||||
trailing: TextButton(
|
||||
onPressed: isUpdating ? null : () => _startEditing('phone', profile),
|
||||
child: const Text('수정'),
|
||||
child: Text(tr('ui.common.edit', fallback: '수정')),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -684,7 +801,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('전화번호', style: TextStyle(fontWeight: FontWeight.w600)),
|
||||
Text(
|
||||
tr('ui.userfront.profile.phone.title', fallback: '전화번호'),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
@@ -710,12 +830,19 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
if (_isPhoneChanged && !_isPhoneVerified)
|
||||
ElevatedButton(
|
||||
onPressed: _isVerifying ? null : _sendCode,
|
||||
child: Text(_isCodeSent ? '재전송' : '인증요청'),
|
||||
child: Text(
|
||||
_isCodeSent
|
||||
? tr('ui.common.resend', fallback: '재전송')
|
||||
: tr(
|
||||
'ui.userfront.profile.phone.request_code',
|
||||
fallback: '인증요청',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton(
|
||||
onPressed: isUpdating ? null : () => _cancelEditing(profile),
|
||||
child: const Text('취소'),
|
||||
child: Text(tr('ui.common.cancel', fallback: '취소')),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -731,26 +858,32 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
keyboardType: TextInputType.number,
|
||||
textInputAction: TextInputAction.done,
|
||||
onSubmitted: (_) => _verifyCode(profile),
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: '인증번호 6자리',
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: tr(
|
||||
'ui.userfront.profile.phone.code_hint',
|
||||
fallback: '인증번호 6자리',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: _isVerifying ? null : () => _verifyCode(profile),
|
||||
child: const Text('확인'),
|
||||
child: Text(tr('ui.common.confirm', fallback: '확인')),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
if (_isPhoneChanged && !_isPhoneVerified)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'휴대폰 번호를 변경하려면 SMS 인증이 필요합니다.',
|
||||
style: TextStyle(color: Colors.orange, fontSize: 12),
|
||||
tr(
|
||||
'msg.userfront.profile.phone.verify_notice',
|
||||
fallback: '휴대폰 번호를 변경하려면 SMS 인증이 필요합니다.',
|
||||
),
|
||||
style: const TextStyle(color: Colors.orange, fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -763,20 +896,26 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'비밀번호 변경',
|
||||
tr('ui.userfront.profile.password.title', fallback: '비밀번호 변경'),
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'현재 비밀번호 확인 후 새 비밀번호로 변경합니다.',
|
||||
style: TextStyle(color: Color(0xFF6B7280)),
|
||||
Text(
|
||||
tr(
|
||||
'msg.userfront.profile.password.subtitle',
|
||||
fallback: '현재 비밀번호 확인 후 새 비밀번호로 변경합니다.',
|
||||
),
|
||||
style: const TextStyle(color: Color(0xFF6B7280)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _currentPasswordController,
|
||||
obscureText: !_showCurrentPassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: '현재 비밀번호',
|
||||
labelText: tr(
|
||||
'ui.userfront.profile.password.current',
|
||||
fallback: '현재 비밀번호',
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_showCurrentPassword ? Icons.visibility_off : Icons.visibility),
|
||||
@@ -791,7 +930,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
controller: _newPasswordController,
|
||||
obscureText: !_showNewPassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: '새 비밀번호',
|
||||
labelText: tr(
|
||||
'ui.userfront.profile.password.new',
|
||||
fallback: '새 비밀번호',
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_showNewPassword ? Icons.visibility_off : Icons.visibility),
|
||||
@@ -806,7 +948,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
controller: _confirmPasswordController,
|
||||
obscureText: !_showConfirmPassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: '새 비밀번호 확인',
|
||||
labelText: tr(
|
||||
'ui.userfront.profile.password.confirm',
|
||||
fallback: '새 비밀번호 확인',
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_showConfirmPassword ? Icons.visibility_off : Icons.visibility),
|
||||
@@ -841,12 +986,22 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('비밀번호 변경'),
|
||||
: Text(
|
||||
tr(
|
||||
'ui.userfront.profile.password.change',
|
||||
fallback: '비밀번호 변경',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
TextButton(
|
||||
onPressed: () => context.go('/recovery'),
|
||||
child: const Text('비밀번호를 잊으셨나요?'),
|
||||
child: Text(
|
||||
tr(
|
||||
'ui.userfront.profile.password.forgot',
|
||||
fallback: '비밀번호를 잊으셨나요?',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -869,55 +1024,88 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
children: [
|
||||
_buildHeaderCard(profile),
|
||||
const SizedBox(height: 28),
|
||||
_buildSectionTitle('기본 정보', '계정 기본 정보를 관리합니다.'),
|
||||
_buildSectionTitle(
|
||||
tr('ui.userfront.profile.section.basic', fallback: '기본 정보'),
|
||||
tr(
|
||||
'msg.userfront.profile.section.basic',
|
||||
fallback: '계정 기본 정보를 관리합니다.',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildCard(
|
||||
Column(
|
||||
children: [
|
||||
_buildEditableTile(
|
||||
field: 'name',
|
||||
label: '이름',
|
||||
label: tr('ui.userfront.profile.field.name', fallback: '이름'),
|
||||
value: profile.name,
|
||||
profile: profile,
|
||||
isUpdating: isUpdating,
|
||||
controller: _nameController!,
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildReadOnlyTile('이메일', profile.email),
|
||||
_buildReadOnlyTile(
|
||||
tr('ui.userfront.profile.field.email', fallback: '이메일'),
|
||||
profile.email,
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildPhoneEditor(profile, isUpdating),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
_buildSectionTitle('조직 정보', '소속 및 구분 정보입니다.'),
|
||||
_buildSectionTitle(
|
||||
tr('ui.userfront.profile.section.organization', fallback: '조직 정보'),
|
||||
tr(
|
||||
'msg.userfront.profile.section.organization',
|
||||
fallback: '소속 및 구분 정보입니다.',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildCard(
|
||||
Column(
|
||||
children: [
|
||||
_buildEditableTile(
|
||||
field: 'department',
|
||||
label: '소속',
|
||||
label: tr('ui.userfront.profile.field.department', fallback: '소속'),
|
||||
value: profile.department,
|
||||
profile: profile,
|
||||
isUpdating: isUpdating,
|
||||
controller: _departmentController!,
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildReadOnlyTile('구분', profile.affiliationType),
|
||||
_buildReadOnlyTile(
|
||||
tr('ui.userfront.profile.field.affiliation', fallback: '구분'),
|
||||
profile.affiliationType,
|
||||
),
|
||||
if (profile.tenant != null) ...[
|
||||
const Divider(height: 24),
|
||||
_buildReadOnlyTile('소속 테넌트', profile.tenant!.name),
|
||||
_buildReadOnlyTile(
|
||||
tr(
|
||||
'ui.userfront.profile.field.tenant',
|
||||
fallback: '소속 테넌트',
|
||||
),
|
||||
profile.tenant!.name,
|
||||
),
|
||||
],
|
||||
if (profile.companyCode.isNotEmpty) ...[
|
||||
const Divider(height: 24),
|
||||
_buildReadOnlyTile('회사코드', profile.companyCode),
|
||||
_buildReadOnlyTile(
|
||||
tr('ui.userfront.profile.field.company_code', fallback: '회사코드'),
|
||||
profile.companyCode,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
_buildSectionTitle('보안', '비밀번호를 안전하게 관리합니다.'),
|
||||
_buildSectionTitle(
|
||||
tr('ui.userfront.profile.section.security', fallback: '보안'),
|
||||
tr(
|
||||
'msg.userfront.profile.section.security',
|
||||
fallback: '비밀번호를 안전하게 관리합니다.',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildPasswordSection(),
|
||||
if (isUpdating || _isVerifying) ...[
|
||||
@@ -943,18 +1131,25 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
final profile = profileState.value ?? _cachedProfile;
|
||||
if (profile == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('내 정보')),
|
||||
appBar: AppBar(
|
||||
title: Text(tr('ui.userfront.nav.profile', fallback: '내 정보')),
|
||||
),
|
||||
body: profileState.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('정보를 불러올 수 없습니다.'),
|
||||
Text(
|
||||
tr(
|
||||
'msg.userfront.profile.load_failed',
|
||||
fallback: '정보를 불러올 수 없습니다.',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () => ref.read(profileProvider.notifier).loadProfile(),
|
||||
child: const Text('재시도'),
|
||||
child: Text(tr('ui.common.retry', fallback: '재시도')),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -971,8 +1166,8 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
backgroundColor: _subtle,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'Baron 로그인',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
tr('ui.userfront.app_title', fallback: 'Baron 로그인'),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
elevation: 0,
|
||||
backgroundColor: _surface,
|
||||
@@ -980,17 +1175,17 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
tooltip: '대시보드',
|
||||
tooltip: tr('ui.userfront.nav.dashboard', fallback: '대시보드'),
|
||||
onPressed: () => context.go('/'),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
tooltip: 'QR 스캔',
|
||||
tooltip: tr('ui.userfront.nav.qr_scan', fallback: 'QR 스캔'),
|
||||
onPressed: () => context.push('/scan'),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.logout),
|
||||
tooltip: '로그아웃',
|
||||
tooltip: tr('ui.userfront.nav.logout', fallback: '로그아웃'),
|
||||
onPressed: _logout,
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user