계정 정보 다이얼로그 추가 및 전체 목록 페치 개선
This commit is contained in:
@@ -44,6 +44,17 @@ class CustomerController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
bool? isPartner;
|
||||
bool? isGeneral;
|
||||
switch (_typeFilter) {
|
||||
@@ -68,7 +79,7 @@ class CustomerController extends ChangeNotifier {
|
||||
};
|
||||
|
||||
final response = await _repository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _pageSize,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
isPartner: isPartner,
|
||||
|
||||
@@ -125,9 +125,10 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -174,7 +175,7 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -301,6 +302,14 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -316,6 +325,15 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -357,8 +375,18 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
}
|
||||
|
||||
void _goToPage(int page) {
|
||||
final result = _controller.result;
|
||||
final int totalPages;
|
||||
if (result == null || result.pageSize == 0) {
|
||||
totalPages = 1;
|
||||
} else {
|
||||
final calculated = (result.total / result.pageSize).ceil();
|
||||
totalPages = calculated < 1 ? 1 : calculated;
|
||||
}
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
} else if (page > totalPages) {
|
||||
page = totalPages;
|
||||
}
|
||||
_updateRoute(page: page);
|
||||
}
|
||||
@@ -647,7 +675,10 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(customerId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -672,7 +703,8 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(false),
|
||||
: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -984,11 +1016,13 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
description: '"${customer.customerName}" 고객사를 삭제하시겠습니까?',
|
||||
actions: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
@@ -1012,10 +1046,14 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -54,6 +54,17 @@ class GroupController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isDefault = switch (_defaultFilter) {
|
||||
GroupDefaultFilter.all => null,
|
||||
GroupDefaultFilter.defaultOnly => true,
|
||||
@@ -65,7 +76,7 @@ class GroupController extends ChangeNotifier {
|
||||
GroupStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _repository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _result?.pageSize ?? 20,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
isDefault: isDefault,
|
||||
|
||||
@@ -100,9 +100,10 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -129,7 +130,7 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -253,6 +254,14 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -268,6 +277,15 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
: () => _controller.fetch(page: currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(page: totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -367,7 +385,10 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(dialogContext).pop(),
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -397,7 +418,10 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(dialogContext);
|
||||
final navigator = Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(groupId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -553,11 +577,17 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
content: Text('"${group.groupName}" 그룹을 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
@@ -582,10 +612,14 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/failure.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/common/utils/pagination_utils.dart';
|
||||
|
||||
import '../../../../../core/permissions/permission_manager.dart';
|
||||
import '../../application/permission_synchronizer.dart';
|
||||
@@ -65,10 +66,13 @@ class GroupPermissionController extends ChangeNotifier {
|
||||
_isLoadingGroups = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final response = await _groupRepository.list(page: 1, pageSize: 200);
|
||||
final groups = await fetchAllPaginatedItems<Group>(
|
||||
request: (page, pageSize) =>
|
||||
_groupRepository.list(page: page, pageSize: pageSize),
|
||||
);
|
||||
_groups
|
||||
..clear()
|
||||
..addAll(response.items);
|
||||
..addAll(groups);
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
@@ -83,14 +87,16 @@ class GroupPermissionController extends ChangeNotifier {
|
||||
_isLoadingMenus = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final response = await _menuRepository.list(
|
||||
page: 1,
|
||||
pageSize: 200,
|
||||
includeDeleted: false,
|
||||
final menus = await fetchAllPaginatedItems<MenuItem>(
|
||||
request: (page, pageSize) => _menuRepository.list(
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
includeDeleted: false,
|
||||
),
|
||||
);
|
||||
_menus
|
||||
..clear()
|
||||
..addAll(response.items);
|
||||
..addAll(menus);
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
@@ -106,13 +112,24 @@ class GroupPermissionController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
GroupPermissionStatusFilter.all => null,
|
||||
GroupPermissionStatusFilter.activeOnly => true,
|
||||
GroupPermissionStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _permissionRepository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _result?.pageSize ?? 20,
|
||||
groupId: _groupFilter,
|
||||
menuId: _menuFilter,
|
||||
|
||||
@@ -151,9 +151,10 @@ class _GroupPermissionEnabledPageState
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -180,7 +181,7 @@ class _GroupPermissionEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -366,6 +367,14 @@ class _GroupPermissionEnabledPageState
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -381,6 +390,15 @@ class _GroupPermissionEnabledPageState
|
||||
: () => _controller.fetch(page: currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(page: totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -481,7 +499,10 @@ class _GroupPermissionEnabledPageState
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(dialogContext).pop(false),
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -513,7 +534,10 @@ class _GroupPermissionEnabledPageState
|
||||
isActive: activeNotifier.value,
|
||||
note: trimmedNote.isEmpty ? null : trimmedNote,
|
||||
);
|
||||
final navigator = Navigator.of(dialogContext);
|
||||
final navigator = Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(permissionId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -758,7 +782,8 @@ class _GroupPermissionEnabledPageState
|
||||
secondaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -766,7 +791,8 @@ class _GroupPermissionEnabledPageState
|
||||
primaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.destructive(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
);
|
||||
},
|
||||
@@ -791,10 +817,14 @@ class _GroupPermissionEnabledPageState
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/failure.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/common/utils/pagination_utils.dart';
|
||||
|
||||
import '../../domain/entities/menu.dart';
|
||||
import '../../domain/repositories/menu_repository.dart';
|
||||
@@ -45,12 +46,14 @@ class MenuController extends ChangeNotifier {
|
||||
_isLoadingParents = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final response = await _repository.list(
|
||||
page: 1,
|
||||
pageSize: 200,
|
||||
includeDeleted: false,
|
||||
final parents = await fetchAllPaginatedItems<MenuItem>(
|
||||
request: (page, pageSize) => _repository.list(
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
includeDeleted: false,
|
||||
),
|
||||
);
|
||||
_parents = response.items;
|
||||
_parents = parents;
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
@@ -66,13 +69,24 @@ class MenuController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
MenuStatusFilter.all => null,
|
||||
MenuStatusFilter.activeOnly => true,
|
||||
MenuStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _repository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _result?.pageSize ?? 20,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
parentId: _parentFilter,
|
||||
|
||||
@@ -121,9 +121,10 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -150,7 +151,7 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -309,6 +310,14 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -324,6 +333,15 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
: () => _controller.fetch(page: currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(page: totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -417,7 +435,10 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(dialogContext).pop(false),
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -464,7 +485,10 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(dialogContext);
|
||||
final navigator = Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(menuId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -706,7 +730,10 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
secondaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -714,7 +741,10 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
primaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.destructive(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(true),
|
||||
child: const Text('삭제'),
|
||||
);
|
||||
},
|
||||
@@ -739,10 +769,14 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/failure.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/common/utils/pagination_utils.dart';
|
||||
|
||||
import '../../../vendor/domain/entities/vendor.dart';
|
||||
import '../../../vendor/domain/repositories/vendor_repository.dart';
|
||||
@@ -61,13 +62,24 @@ class ProductController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
ProductStatusFilter.all => null,
|
||||
ProductStatusFilter.activeOnly => true,
|
||||
ProductStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _productRepository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _pageSize,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
vendorId: _vendorFilter,
|
||||
@@ -92,10 +104,16 @@ class ProductController extends ChangeNotifier {
|
||||
_isLoadingLookups = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final vendorResult = await _vendorRepository.list(page: 1, pageSize: 100);
|
||||
final uomResult = await _uomRepository.list(page: 1, pageSize: 100);
|
||||
_vendorOptions = vendorResult.items;
|
||||
_uomOptions = uomResult.items;
|
||||
final vendors = await fetchAllPaginatedItems<Vendor>(
|
||||
request: (page, pageSize) =>
|
||||
_vendorRepository.list(page: page, pageSize: pageSize),
|
||||
);
|
||||
final uoms = await fetchAllPaginatedItems<Uom>(
|
||||
request: (page, pageSize) =>
|
||||
_uomRepository.list(page: page, pageSize: pageSize),
|
||||
);
|
||||
_vendorOptions = vendors;
|
||||
_uomOptions = uoms;
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
|
||||
@@ -125,9 +125,10 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -154,7 +155,7 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -316,6 +317,14 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -331,6 +340,15 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -428,8 +446,18 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
}
|
||||
|
||||
void _goToPage(int page) {
|
||||
final result = _controller.result;
|
||||
final int totalPages;
|
||||
if (result == null || result.pageSize == 0) {
|
||||
totalPages = 1;
|
||||
} else {
|
||||
final calculated = (result.total / result.pageSize).ceil();
|
||||
totalPages = calculated < 1 ? 1 : calculated;
|
||||
}
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
} else if (page > totalPages) {
|
||||
page = totalPages;
|
||||
}
|
||||
_updateRoute(page: page);
|
||||
}
|
||||
@@ -579,7 +607,10 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(productId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -602,7 +633,8 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(false),
|
||||
: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -835,11 +867,13 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
title: '제품 삭제',
|
||||
description: '"${product.productName}" 제품을 삭제하시겠습니까?',
|
||||
primaryAction: ShadButton.destructive(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
secondaryAction: ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
),
|
||||
@@ -862,10 +896,14 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -201,25 +201,16 @@ class _UomAutocompleteFieldState extends State<UomAutocompleteField> {
|
||||
}
|
||||
|
||||
void _setSelection(Uom? uom, {bool notify = true}) {
|
||||
_selected = uom;
|
||||
if (uom != null && !_baseOptions.any((item) => item.id == uom.id)) {
|
||||
_baseOptions.add(uom);
|
||||
}
|
||||
_isApplyingText = true;
|
||||
if (uom == null) {
|
||||
_controller.clear();
|
||||
if (notify) {
|
||||
widget.onSelected(null);
|
||||
}
|
||||
} else {
|
||||
_controller
|
||||
..text = uom.uomName
|
||||
..selection = TextSelection.collapsed(offset: uom.uomName.length);
|
||||
if (notify) {
|
||||
widget.onSelected(uom.id);
|
||||
setState(() {
|
||||
_selected = uom;
|
||||
if (uom != null && !_baseOptions.any((item) => item.id == uom.id)) {
|
||||
_baseOptions.add(uom);
|
||||
}
|
||||
_applyControllerText(uom?.uomName ?? '');
|
||||
});
|
||||
if (notify) {
|
||||
widget.onSelected(uom?.id);
|
||||
}
|
||||
_isApplyingText = false;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -247,6 +238,7 @@ class _UomAutocompleteFieldState extends State<UomAutocompleteField> {
|
||||
displayStringForOption: (option) => option.uomName,
|
||||
onSelected: (uom) => _setSelection(uom),
|
||||
fieldViewBuilder: (context, textController, focusNode, onFieldSubmitted) {
|
||||
assert(identical(textController, _controller));
|
||||
final placeholder = widget.placeholder ?? const Text('단위를 선택하세요');
|
||||
return Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
@@ -312,4 +304,11 @@ class _UomAutocompleteFieldState extends State<UomAutocompleteField> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _applyControllerText(String value) {
|
||||
_isApplyingText = true;
|
||||
_controller.text = value;
|
||||
_controller.selection = TextSelection.collapsed(offset: value.length);
|
||||
_isApplyingText = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,26 +209,20 @@ class _VendorAutocompleteFieldState extends State<VendorAutocompleteField> {
|
||||
}
|
||||
|
||||
void _setSelection(Vendor? vendor, {bool notify = true}) {
|
||||
_selected = vendor;
|
||||
if (vendor != null && !_baseOptions.any((item) => item.id == vendor.id)) {
|
||||
_baseOptions.add(vendor);
|
||||
}
|
||||
_isApplyingText = true;
|
||||
if (vendor == null) {
|
||||
_controller.clear();
|
||||
if (notify) {
|
||||
widget.onSelected(null);
|
||||
setState(() {
|
||||
_selected = vendor;
|
||||
if (vendor != null && !_baseOptions.any((item) => item.id == vendor.id)) {
|
||||
_baseOptions.add(vendor);
|
||||
}
|
||||
} else {
|
||||
final label = _displayLabel(vendor);
|
||||
_controller
|
||||
..text = label
|
||||
..selection = TextSelection.collapsed(offset: label.length);
|
||||
if (notify) {
|
||||
widget.onSelected(vendor.id);
|
||||
if (vendor == null) {
|
||||
_applyControllerText('');
|
||||
} else {
|
||||
_applyControllerText(_displayLabel(vendor));
|
||||
}
|
||||
});
|
||||
if (notify) {
|
||||
widget.onSelected(vendor?.id);
|
||||
}
|
||||
_isApplyingText = false;
|
||||
}
|
||||
|
||||
String _displayLabel(Vendor vendor) {
|
||||
@@ -261,6 +255,7 @@ class _VendorAutocompleteFieldState extends State<VendorAutocompleteField> {
|
||||
displayStringForOption: _displayLabel,
|
||||
onSelected: (vendor) => _setSelection(vendor),
|
||||
fieldViewBuilder: (context, textController, focusNode, onFieldSubmitted) {
|
||||
assert(identical(textController, _controller));
|
||||
final placeholder = widget.placeholder ?? const Text('제조사를 선택하세요');
|
||||
return Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
@@ -326,4 +321,11 @@ class _VendorAutocompleteFieldState extends State<VendorAutocompleteField> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _applyControllerText(String value) {
|
||||
_isApplyingText = true;
|
||||
_controller.text = value;
|
||||
_controller.selection = TextSelection.collapsed(offset: value.length);
|
||||
_isApplyingText = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/failure.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/common/utils/pagination_utils.dart';
|
||||
|
||||
import '../../../../../core/permissions/permission_manager.dart';
|
||||
import '../../../group/domain/entities/group.dart';
|
||||
@@ -55,8 +56,11 @@ class UserController extends ChangeNotifier {
|
||||
_isLoadingGroups = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final response = await _groupRepository.list(page: 1, pageSize: 100);
|
||||
_groups = response.items;
|
||||
final groups = await fetchAllPaginatedItems<Group>(
|
||||
request: (page, pageSize) =>
|
||||
_groupRepository.list(page: page, pageSize: pageSize),
|
||||
);
|
||||
_groups = groups;
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
@@ -72,13 +76,24 @@ class UserController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
UserStatusFilter.all => null,
|
||||
UserStatusFilter.activeOnly => true,
|
||||
UserStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _userRepository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _result?.pageSize ?? 20,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
groupId: _groupFilter,
|
||||
|
||||
@@ -133,9 +133,10 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -162,7 +163,7 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -293,6 +294,14 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -308,6 +317,15 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
: () => _controller.fetch(page: currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(page: totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -423,7 +441,10 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final input = UserInput(
|
||||
employeeNo: code,
|
||||
employeeName: name,
|
||||
@@ -454,7 +475,10 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
secondaryAction: ValueListenableBuilder<bool>(
|
||||
valueListenable: saving,
|
||||
builder: (context, isSaving, _) {
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving ? null : () => navigator.pop(false),
|
||||
child: const Text('취소'),
|
||||
@@ -666,11 +690,13 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
description: '"${user.employeeName}" 사용자를 삭제하시겠습니까?',
|
||||
actions: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
@@ -694,10 +720,14 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
List<Widget> _buildAuditInfo(UserAccount user, ShadThemeData theme) {
|
||||
|
||||
@@ -41,13 +41,24 @@ class VendorController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
VendorStatusFilter.all => null,
|
||||
VendorStatusFilter.activeOnly => true,
|
||||
VendorStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _repository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _pageSize,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
isActive: isActive,
|
||||
|
||||
@@ -85,7 +85,7 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
final DateFormat _dateFormat = DateFormat('yyyy-MM-dd HH:mm');
|
||||
String? _lastError;
|
||||
bool _routeApplied = false;
|
||||
String? _lastRouteSignature;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -97,9 +97,14 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (!_routeApplied) {
|
||||
_routeApplied = true;
|
||||
_applyRouteParameters();
|
||||
_syncWithRoute(widget.routeUri);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _VendorEnabledPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.routeUri != widget.routeUri) {
|
||||
_syncWithRoute(widget.routeUri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +148,7 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -244,6 +249,14 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -259,6 +272,14 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -308,8 +329,17 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
}
|
||||
}
|
||||
|
||||
void _applyRouteParameters() {
|
||||
final params = widget.routeUri.queryParameters;
|
||||
void _syncWithRoute(Uri routeUri) {
|
||||
final signature = routeUri.toString();
|
||||
if (_lastRouteSignature == signature) {
|
||||
return;
|
||||
}
|
||||
_lastRouteSignature = signature;
|
||||
_applyRouteParameters(routeUri);
|
||||
}
|
||||
|
||||
void _applyRouteParameters(Uri routeUri) {
|
||||
final params = routeUri.queryParameters;
|
||||
final query = params['q'] ?? '';
|
||||
final status = _statusFromParam(params['status']);
|
||||
final pageSizeParam = int.tryParse(params['page_size'] ?? '');
|
||||
@@ -327,8 +357,14 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
}
|
||||
|
||||
void _goToPage(int page) {
|
||||
final result = _controller.result;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
} else if (page > totalPages) {
|
||||
page = totalPages;
|
||||
}
|
||||
_updateRoute(page: page);
|
||||
}
|
||||
@@ -448,7 +484,10 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(vendorId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -471,7 +510,8 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(false),
|
||||
: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -613,11 +653,13 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
description: '"${vendor.vendorName}" 벤더를 삭제하시겠습니까?',
|
||||
actions: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
@@ -641,10 +683,15 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
// 스캐폴드 메신저가 없는 경우에는 중단하여 런타임 오류를 방지한다.
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
@@ -39,13 +39,24 @@ class WarehouseController extends ChangeNotifier {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
if (page < 1) {
|
||||
resolvedPage = 1;
|
||||
} else if (previous != null && previous.pageSize > 0) {
|
||||
final calculated = (previous.total / previous.pageSize).ceil();
|
||||
final maxPage = calculated < 1 ? 1 : calculated;
|
||||
resolvedPage = page > maxPage ? maxPage : page;
|
||||
} else {
|
||||
resolvedPage = page;
|
||||
}
|
||||
final isActive = switch (_statusFilter) {
|
||||
WarehouseStatusFilter.all => null,
|
||||
WarehouseStatusFilter.activeOnly => true,
|
||||
WarehouseStatusFilter.inactiveOnly => false,
|
||||
};
|
||||
final response = await _repository.list(
|
||||
page: page,
|
||||
page: resolvedPage,
|
||||
pageSize: _pageSize,
|
||||
query: _query.isEmpty ? null : _query,
|
||||
isActive: isActive,
|
||||
|
||||
@@ -122,9 +122,10 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
final error = _controller.errorMessage;
|
||||
if (error != null && error != _lastError && mounted) {
|
||||
_lastError = error;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(error)));
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(error)));
|
||||
}
|
||||
_controller.clearError();
|
||||
}
|
||||
}
|
||||
@@ -151,7 +152,7 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -255,6 +256,14 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
@@ -270,6 +279,15 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -341,8 +359,18 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
}
|
||||
|
||||
void _goToPage(int page) {
|
||||
final result = _controller.result;
|
||||
final int totalPages;
|
||||
if (result == null || result.pageSize == 0) {
|
||||
totalPages = 1;
|
||||
} else {
|
||||
final calculated = (result.total / result.pageSize).ceil();
|
||||
totalPages = calculated < 1 ? 1 : calculated;
|
||||
}
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
} else if (page > totalPages) {
|
||||
page = totalPages;
|
||||
}
|
||||
_updateRoute(page: page);
|
||||
}
|
||||
@@ -522,7 +550,10 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(context);
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(warehouseId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -545,7 +576,8 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(false),
|
||||
: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -812,11 +844,13 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
description: '"${warehouse.warehouseName}" 창고를 삭제하시겠습니까?',
|
||||
actions: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
@@ -840,10 +874,14 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
}
|
||||
|
||||
void _showSnack(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
messenger.showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime? value) {
|
||||
|
||||
Reference in New Issue
Block a user