초기 커밋

This commit is contained in:
2026-03-01 07:55:59 +09:00
commit b0262d6bab
67 changed files with 4660 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:gap/gap.dart';
import 'package:pluto_grid/pluto_grid.dart';
import '../../../../core/utils/extensions.dart';
import '../../../../shared/widgets/confirm_dialog.dart';
class UserManagementScreen extends ConsumerStatefulWidget {
const UserManagementScreen({super.key});
@override
ConsumerState<UserManagementScreen> createState() =>
_UserManagementScreenState();
}
class _UserManagementScreenState extends ConsumerState<UserManagementScreen> {
late PlutoGridStateManager stateManager;
// Mock 데이터
final List<Map<String, dynamic>> _mockUsers = List.generate(
20,
(i) => {
'id': '${i + 1}',
'name': '사용자 ${i + 1}',
'email': 'user${i + 1}@example.com',
'role': i == 0 ? 'admin' : 'user',
'status': i % 5 == 0 ? '비활성' : '활성',
'created_at': '2025-01-${(i + 1).toString().padLeft(2, '0')}',
},
);
List<PlutoColumn> get _columns => [
PlutoColumn(
title: 'ID',
field: 'id',
type: PlutoColumnType.text(),
width: 60,
enableEditingMode: false,
),
PlutoColumn(
title: '이름',
field: 'name',
type: PlutoColumnType.text(),
width: 150,
),
PlutoColumn(
title: '이메일',
field: 'email',
type: PlutoColumnType.text(),
width: 250,
),
PlutoColumn(
title: '역할',
field: 'role',
type: PlutoColumnType.select(['admin', 'user']),
width: 100,
),
PlutoColumn(
title: '상태',
field: 'status',
type: PlutoColumnType.select(['활성', '비활성']),
width: 100,
),
PlutoColumn(
title: '가입일',
field: 'created_at',
type: PlutoColumnType.text(),
width: 150,
enableEditingMode: false,
),
];
List<PlutoRow> get _rows => _mockUsers
.map(
(user) => PlutoRow(
cells: {
'id': PlutoCell(value: user['id']),
'name': PlutoCell(value: user['name']),
'email': PlutoCell(value: user['email']),
'role': PlutoCell(value: user['role']),
'status': PlutoCell(value: user['status']),
'created_at': PlutoCell(value: user['created_at']),
},
),
)
.toList();
void _showCreateUserDialog() {
final nameController = TextEditingController();
final emailController = TextEditingController();
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('새 사용자 추가'),
content: SizedBox(
width: 400,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: '이름',
hintText: '사용자 이름 입력',
),
),
const Gap(16),
TextField(
controller: emailController,
decoration: const InputDecoration(
labelText: '이메일',
hintText: 'email@example.com',
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('취소'),
),
FilledButton(
onPressed: () {
// TODO: API 연동
Navigator.pop(context);
if (mounted) {
context.showSnackBar('사용자가 추가되었습니다');
}
},
child: const Text('추가'),
),
],
),
);
}
Future<void> _deleteUser(PlutoRow row) async {
final confirmed = await ConfirmDialog.show(
context,
title: '사용자 삭제',
message: '정말로 이 사용자를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.',
confirmLabel: '삭제',
isDestructive: true,
);
if (confirmed == true) {
stateManager.removeRows([row]);
if (mounted) {
context.showSnackBar('사용자가 삭제되었습니다');
}
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 헤더
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'사용자 관리',
style: context.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const Gap(4),
Text(
'사용자를 조회하고 관리하세요',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colorScheme.onSurfaceVariant,
),
),
],
),
FilledButton.icon(
onPressed: _showCreateUserDialog,
icon: const Icon(Icons.person_add),
label: const Text('새 사용자'),
),
],
),
const Gap(24),
// 데이터 그리드
Expanded(
child: Card(
clipBehavior: Clip.antiAlias,
child: PlutoGrid(
columns: _columns,
rows: _rows,
onLoaded: (event) {
stateManager = event.stateManager;
},
onRowDoubleTap: (event) {
// TODO: 사용자 상세 정보 다이얼로그
},
configuration: PlutoGridConfiguration(
style: PlutoGridStyleConfig(
gridBackgroundColor:
context.colorScheme.surface,
rowColor: context.colorScheme.surface,
activatedColor:
context.colorScheme.primaryContainer,
activatedBorderColor: context.colorScheme.primary,
gridBorderColor:
context.colorScheme.outlineVariant,
borderColor:
context.colorScheme.outlineVariant,
cellTextStyle:
context.textTheme.bodyMedium!,
columnTextStyle:
context.textTheme.titleSmall!.copyWith(
fontWeight: FontWeight.bold,
),
),
columnSize: const PlutoGridColumnSizeConfig(
autoSizeMode: PlutoAutoSizeMode.scale,
),
),
),
),
),
],
),
);
}
}