1
0
forked from baron/baron-sso

App 카드 크기 조정 및 상세 보기 수정

This commit is contained in:
2026-04-08 14:19:35 +09:00
parent 24f477a28e
commit f4b1c449b1
6 changed files with 251 additions and 87 deletions

View File

@@ -38,6 +38,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
static const _border = Color(0xFFE5E7EB);
static const _subtle = Color(0xFFF7F8FA);
static const double _dashboardCardSpacing = 12;
static const double _dashboardCardMaxWidth = 228;
static const double _activityDialogMaxWidth = 360;
static const double _historySessionMinWidth = 92;
static const double _historyOtherColumnsBaselineWidth = 780;
static const int _historySessionMinVisibleChars = 8;
@@ -235,85 +237,158 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
context: context,
builder: (context) => Consumer(
builder: (context, ref, _) {
final dialogWidth = math.min(
MediaQuery.sizeOf(context).width - 48,
_activityDialogMaxWidth,
);
final statusLabel = item.status == 'active'
? tr('ui.userfront.dashboard.activity.linked')
: tr('ui.userfront.dashboard.status.revoked');
final statusColor = _activityStatusColor(item.status);
return AlertDialog(
title: Text(item.appName),
backgroundColor: _surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
insetPadding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 24,
),
contentPadding: const EdgeInsets.fromLTRB(20, 20, 20, 8),
content: SizedBox(
width: double.maxFinite,
width: dialogWidth,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tr('ui.userfront.dashboard.scopes.title'),
style: const TextStyle(fontWeight: FontWeight.bold),
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _subtle,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: _border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.appName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: _ink,
),
),
const SizedBox(height: 4),
Text(
tr('ui.common.details'),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.grey[600],
),
),
],
),
),
const SizedBox(height: 8),
if (item.scopes.isEmpty)
Text(
tr('msg.userfront.dashboard.scopes.empty'),
style: const TextStyle(color: Colors.grey),
)
else
Wrap(
spacing: 8,
runSpacing: 4,
children: item.scopes
.map(
(s) => Chip(
label: Text(
s,
style: const TextStyle(fontSize: 12),
),
visualDensity: VisualDensity.compact,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
const SizedBox(height: 16),
_buildActivityDetailSection(
title: tr('ui.userfront.dashboard.status_history'),
child: Row(
children: [
Expanded(
child: _buildActivityDetailField(
label: tr(
'ui.userfront.dashboard.link_status_label',
),
value: statusLabel,
valueColor: statusColor,
),
),
const SizedBox(width: 10),
Expanded(
child: _buildActivityDetailField(
label: tr('ui.userfront.dashboard.last_auth_label'),
value: item.lastAuthAt,
),
),
],
),
),
const SizedBox(height: 12),
_buildActivityDetailSection(
title: tr('ui.userfront.dashboard.scopes.title'),
child: item.scopes.isEmpty
? Text(
tr('msg.userfront.dashboard.scopes.empty'),
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
)
.toList(),
),
const SizedBox(height: 24),
Text(
tr('ui.userfront.dashboard.status_history'),
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tr(
'msg.userfront.dashboard.last_auth',
params: {'value': item.lastAuthAt},
),
),
const SizedBox(height: 4),
Builder(
builder: (context) {
final statusLabel = item.status == 'active'
? tr('ui.common.status.active')
: tr('ui.userfront.dashboard.status.revoked');
return Text(
tr(
'msg.userfront.dashboard.current_status',
params: {'status': statusLabel},
),
style: TextStyle(
color: item.status == 'active'
? Colors.green
: Colors.grey,
),
);
},
),
],
: Wrap(
spacing: 8,
runSpacing: 8,
children: item.scopes
.map(
(scope) => Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
decoration: BoxDecoration(
color: _subtle,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: _border),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.shield_outlined,
size: 14,
color: _ink,
),
const SizedBox(width: 6),
Text(
scope,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: _ink,
),
),
],
),
),
)
.toList(),
),
),
],
),
),
actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(tr('ui.common.close')),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () => Navigator.of(context).pop(),
style: TextButton.styleFrom(
foregroundColor: _ink,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
backgroundColor: _subtle,
),
child: Text(
tr('ui.common.close'),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
],
);
@@ -322,6 +397,73 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
);
}
Widget _buildActivityDetailSection({
required String title,
required Widget child,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: _border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: _ink,
),
),
const SizedBox(height: 10),
child,
],
),
);
}
Widget _buildActivityDetailField({
required String label,
required String value,
Color? valueColor,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _subtle,
borderRadius: BorderRadius.circular(14),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Colors.grey[600],
),
),
const SizedBox(height: 6),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: valueColor ?? _ink,
),
),
],
),
);
}
Widget _buildSideMenu(BuildContext context, {required bool closeOnTap}) {
return SafeArea(
child: Column(
@@ -1319,7 +1461,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
Widget _buildActivityCard(_ActivityItem item, {double? cardWidth}) {
final isActive = item.status == 'active';
final statusColor = isActive ? Colors.green : Colors.grey;
final statusColor = _activityStatusColor(item.status);
final borderColor = isActive
? Colors.green.withValues(alpha: 128)
: _border;
@@ -1331,10 +1473,10 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
// 카드 컨텐츠
final cardContent = Container(
width: cardWidth ?? 260,
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: borderColor, width: borderWidth),
boxShadow: isActive
? [
@@ -1355,7 +1497,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
child: Text(
item.appName,
style: const TextStyle(
fontSize: 16,
fontSize: 15,
fontWeight: FontWeight.w600,
color: _ink,
),
@@ -1380,7 +1522,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
),
],
),
const SizedBox(height: 12),
const SizedBox(height: 10),
Text(
tr('ui.userfront.dashboard.last_auth_label'),
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
@@ -1389,12 +1531,12 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
Text(
item.lastAuthAt,
style: const TextStyle(
fontSize: 14,
fontSize: 13,
fontWeight: FontWeight.w600,
color: _ink,
),
),
const SizedBox(height: 16),
const SizedBox(height: 14),
Row(
children: [
Expanded(
@@ -1403,7 +1545,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
style: OutlinedButton.styleFrom(
foregroundColor: _ink,
side: const BorderSide(color: _border),
padding: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(vertical: 7),
),
child: Text(
tr('ui.common.details'),
@@ -1425,7 +1567,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
color: item.isRevoked ? Colors.grey : Colors.redAccent,
width: 0.5,
),
padding: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(vertical: 7),
),
child: _isRevoking && !item.isRevoked
? const SizedBox(
@@ -1783,8 +1925,15 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
}
double _dashboardCardWidth(double maxWidth, int crossAxisCount) {
return (maxWidth - (_dashboardCardSpacing * (crossAxisCount - 1))) /
crossAxisCount;
return math.min(
(maxWidth - (_dashboardCardSpacing * (crossAxisCount - 1))) /
crossAxisCount,
_dashboardCardMaxWidth,
);
}
Color _activityStatusColor(String status) {
return status == 'active' ? Colors.green : Colors.grey;
}
Widget _buildCenteredHistoryHeader(String label, {double? width}) {