feat(pagination): 공통 컨트롤 도입과 사용자 관리 가이드 추가
- 테이블 푸터에서 SuperportPaginationControls를 사용하도록 각 관리 페이지 페이지네이션 로직을 정리 - SuperportPaginationControls 위젯을 추가하고 SuperportTable 푸터를 개선해 페이지 사이즈 선택과 이동 버튼을 분리 - 사용자 등록·계정 관리 요구사항을 문서화한 doc/user_setting.md를 작성하고 AGENTS.md 코멘트 규칙을 업데이트 - flutter analyze를 수행해 빌드 경고가 없음을 확인
This commit is contained in:
@@ -13,6 +13,7 @@ import '../../../../widgets/components/feedback.dart';
|
||||
import '../../../../widgets/components/filter_bar.dart';
|
||||
import '../../../../widgets/components/superport_dialog.dart';
|
||||
import '../../../../widgets/components/superport_table.dart';
|
||||
import '../../../../widgets/components/superport_pagination_controls.dart';
|
||||
import '../../../../widgets/components/feature_disabled_placeholder.dart';
|
||||
import '../../domain/entities/approval.dart';
|
||||
import '../../domain/entities/approval_template.dart';
|
||||
@@ -212,9 +213,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
final isLoadingActions = _controller.isLoadingActions;
|
||||
final isPerformingAction = _controller.isPerformingAction;
|
||||
final processingStepId = _controller.processingStepId;
|
||||
@@ -349,44 +347,11 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
'페이지 $currentPage / $totalPages',
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoadingList || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoadingList || currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoadingList || !hasNext
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoadingList ||
|
||||
currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(page: totalPages),
|
||||
child: const Text('마지막'),
|
||||
),
|
||||
],
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoadingList,
|
||||
onPageSelected: (page) => _controller.fetch(page: page),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -10,6 +10,7 @@ import '../../../../../core/permissions/permission_resources.dart';
|
||||
import '../../../../../widgets/app_layout.dart';
|
||||
import '../../../../../widgets/components/filter_bar.dart';
|
||||
import '../../../../../widgets/components/superport_dialog.dart';
|
||||
import '../../../../../widgets/components/superport_pagination_controls.dart';
|
||||
import '../../../../../widgets/components/feature_disabled_placeholder.dart';
|
||||
import '../controllers/approval_step_controller.dart';
|
||||
import '../../domain/entities/approval_step_input.dart';
|
||||
@@ -119,9 +120,6 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
final totalPages = pageSize == 0
|
||||
? 1
|
||||
: (totalCount / pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
final statusOptions = _buildStatusOptions(records);
|
||||
final selectedStatus = _controller.statusId ?? -1;
|
||||
final approverOptions = _buildApproverOptions(records);
|
||||
@@ -411,54 +409,12 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
Text('총 $totalCount건', style: theme.textTheme.small),
|
||||
Row(
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading ||
|
||||
isSaving ||
|
||||
currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(page: 1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading ||
|
||||
isSaving ||
|
||||
currentPage <= 1
|
||||
? null
|
||||
: () => _controller.fetch(
|
||||
page: currentPage - 1,
|
||||
),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading ||
|
||||
isSaving ||
|
||||
!hasNext
|
||||
? null
|
||||
: () => _controller.fetch(
|
||||
page: currentPage + 1,
|
||||
),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading ||
|
||||
isSaving ||
|
||||
currentPage >= totalPages
|
||||
? null
|
||||
: () => _controller.fetch(
|
||||
page: totalPages,
|
||||
),
|
||||
child: const Text('마지막'),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading || isSaving,
|
||||
onPageSelected: (page) =>
|
||||
_controller.fetch(page: page),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
|
||||
@@ -645,10 +645,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
return records;
|
||||
}
|
||||
|
||||
List<String> _buildRecordRow(InboundRecord record) {
|
||||
List<String> _buildRecordRow(InboundRecord record, int displayIndex) {
|
||||
final primaryItem = record.items.isNotEmpty ? record.items.first : null;
|
||||
return [
|
||||
record.number.split('-').last,
|
||||
displayIndex.toString(),
|
||||
_dateFormatter.format(record.processedAt),
|
||||
record.warehouse,
|
||||
record.transactionNumber,
|
||||
@@ -670,13 +670,15 @@ class _InboundPageState extends State<InboundPage> {
|
||||
DeviceBreakpoint breakpoint,
|
||||
) {
|
||||
final visibleColumns = _visibleColumnsFor(breakpoint);
|
||||
final baseOffset = _rowNumberOffset(records.length);
|
||||
return ShadTable.list(
|
||||
header: [
|
||||
for (final index in visibleColumns)
|
||||
ShadTableCell.header(child: Text(InboundTableSpec.headers[index])),
|
||||
],
|
||||
children: [
|
||||
for (final record in records) _buildTableCells(record, visibleColumns),
|
||||
for (var i = 0; i < records.length; i++)
|
||||
_buildTableCells(records[i], visibleColumns, baseOffset + i + 1),
|
||||
],
|
||||
columnSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(InboundTableSpec.columnSpanWidth),
|
||||
@@ -718,8 +720,9 @@ class _InboundPageState extends State<InboundPage> {
|
||||
List<ShadTableCell> _buildTableCells(
|
||||
InboundRecord record,
|
||||
List<int> visibleColumns,
|
||||
int displayIndex,
|
||||
) {
|
||||
final values = _buildRecordRow(record);
|
||||
final values = _buildRecordRow(record, displayIndex);
|
||||
return [
|
||||
for (final index in visibleColumns)
|
||||
ShadTableCell(
|
||||
@@ -728,6 +731,16 @@ class _InboundPageState extends State<InboundPage> {
|
||||
];
|
||||
}
|
||||
|
||||
int _rowNumberOffset(int currentCount) {
|
||||
final page = _result?.page ?? _currentPage;
|
||||
final pageSize = _result?.pageSize ?? _pageSize;
|
||||
final safePage = page > 0 ? page : 1;
|
||||
final safePageSize = pageSize > 0
|
||||
? pageSize
|
||||
: (currentCount > 0 ? currentCount : 1);
|
||||
return (safePage - 1) * safePageSize;
|
||||
}
|
||||
|
||||
Future<void> _showDetailDialog(InboundRecord record) async {
|
||||
await showInventoryTransactionDetailDialog<void>(
|
||||
context: context,
|
||||
|
||||
@@ -574,39 +574,46 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
style: theme.textTheme.muted,
|
||||
),
|
||||
)
|
||||
: ShadTable.list(
|
||||
header: OutboundTableSpec.headers
|
||||
.map(
|
||||
(header) =>
|
||||
ShadTableCell.header(child: Text(header)),
|
||||
)
|
||||
.toList(),
|
||||
children: [
|
||||
for (final row in visibleRecords.map(
|
||||
_buildRecordRow,
|
||||
))
|
||||
[
|
||||
for (final value in row)
|
||||
ShadTableCell(
|
||||
child: Text(
|
||||
value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
: () {
|
||||
final baseOffset = _rowNumberOffset(
|
||||
visibleRecords.length,
|
||||
);
|
||||
return ShadTable.list(
|
||||
header: OutboundTableSpec.headers
|
||||
.map(
|
||||
(header) =>
|
||||
ShadTableCell.header(child: Text(header)),
|
||||
)
|
||||
.toList(),
|
||||
children: [
|
||||
for (var i = 0; i < visibleRecords.length; i++)
|
||||
[
|
||||
for (final value in _buildRecordRow(
|
||||
visibleRecords[i],
|
||||
baseOffset + i + 1,
|
||||
))
|
||||
ShadTableCell(
|
||||
child: Text(
|
||||
value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
columnSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
OutboundTableSpec.columnSpanWidth,
|
||||
),
|
||||
rowSpanExtent: (index) => const FixedTableSpanExtent(
|
||||
OutboundTableSpec.rowSpanHeight,
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
columnSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
OutboundTableSpec.columnSpanWidth,
|
||||
),
|
||||
rowSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
OutboundTableSpec.rowSpanHeight,
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
);
|
||||
}(),
|
||||
),
|
||||
if (filtered.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
@@ -787,10 +794,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _buildRecordRow(OutboundRecord record) {
|
||||
List<String> _buildRecordRow(OutboundRecord record, int displayIndex) {
|
||||
final primaryItem = record.items.isNotEmpty ? record.items.first : null;
|
||||
return [
|
||||
record.number.split('-').last,
|
||||
displayIndex.toString(),
|
||||
_dateFormatter.format(record.processedAt),
|
||||
record.warehouse,
|
||||
record.transactionNumber,
|
||||
@@ -808,6 +815,16 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
];
|
||||
}
|
||||
|
||||
int _rowNumberOffset(int currentCount) {
|
||||
final page = _result?.page ?? _currentPage;
|
||||
final pageSize = _result?.pageSize ?? _pageSize;
|
||||
final safePage = page > 0 ? page : 1;
|
||||
final safePageSize = pageSize > 0
|
||||
? pageSize
|
||||
: (currentCount > 0 ? currentCount : 1);
|
||||
return (safePage - 1) * safePageSize;
|
||||
}
|
||||
|
||||
Future<void> _showDetailDialog(OutboundRecord record) async {
|
||||
await showInventoryTransactionDetailDialog<void>(
|
||||
context: context,
|
||||
|
||||
@@ -522,36 +522,45 @@ class _RentalPageState extends State<RentalPage> {
|
||||
: '대여 데이터가 없습니다.',
|
||||
description: _errorMessage ?? '검색 조건을 조정해 다시 시도하세요.',
|
||||
)
|
||||
: ShadTable.list(
|
||||
header: RentalTableSpec.headers
|
||||
.map(
|
||||
(header) =>
|
||||
ShadTableCell.header(child: Text(header)),
|
||||
)
|
||||
.toList(),
|
||||
children: [
|
||||
for (final record in visibleRecords)
|
||||
_buildRecordRow(record).map(
|
||||
(value) => ShadTableCell(
|
||||
child: Text(
|
||||
value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
: () {
|
||||
final baseOffset = _rowNumberOffset(
|
||||
visibleRecords.length,
|
||||
);
|
||||
return ShadTable.list(
|
||||
header: RentalTableSpec.headers
|
||||
.map(
|
||||
(header) =>
|
||||
ShadTableCell.header(child: Text(header)),
|
||||
)
|
||||
.toList(),
|
||||
children: [
|
||||
for (var i = 0; i < visibleRecords.length; i++)
|
||||
_buildRecordRow(
|
||||
visibleRecords[i],
|
||||
baseOffset + i + 1,
|
||||
).map(
|
||||
(value) => ShadTableCell(
|
||||
child: Text(
|
||||
value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
columnSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
RentalTableSpec.columnSpanWidth,
|
||||
),
|
||||
rowSpanExtent: (index) => const FixedTableSpanExtent(
|
||||
RentalTableSpec.rowSpanHeight,
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
),
|
||||
],
|
||||
columnSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
RentalTableSpec.columnSpanWidth,
|
||||
),
|
||||
rowSpanExtent: (index) =>
|
||||
const FixedTableSpanExtent(
|
||||
RentalTableSpec.rowSpanHeight,
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
);
|
||||
}(),
|
||||
),
|
||||
if (filtered.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
@@ -747,9 +756,9 @@ class _RentalPageState extends State<RentalPage> {
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _buildRecordRow(RentalRecord record) {
|
||||
List<String> _buildRecordRow(RentalRecord record, int displayIndex) {
|
||||
return [
|
||||
record.number.split('-').last,
|
||||
displayIndex.toString(),
|
||||
_dateFormatter.format(record.processedAt),
|
||||
record.warehouse,
|
||||
record.rentalType,
|
||||
@@ -765,6 +774,16 @@ class _RentalPageState extends State<RentalPage> {
|
||||
];
|
||||
}
|
||||
|
||||
int _rowNumberOffset(int currentCount) {
|
||||
final page = _result?.page ?? _currentPage;
|
||||
final pageSize = _result?.pageSize ?? _pageSize;
|
||||
final safePage = page > 0 ? page : 1;
|
||||
final safePageSize = pageSize > 0
|
||||
? pageSize
|
||||
: (currentCount > 0 ? currentCount : 1);
|
||||
return (safePage - 1) * safePageSize;
|
||||
}
|
||||
|
||||
Future<void> _showDetailDialog(RentalRecord record) async {
|
||||
await showInventoryTransactionDetailDialog<void>(
|
||||
context: context,
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
import 'package:superport_v2/features/util/postal_search/presentation/models/postal_search_result.dart';
|
||||
import 'package:superport_v2/features/util/postal_search/presentation/widgets/postal_search_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/responsive_section.dart';
|
||||
@@ -191,9 +192,6 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -323,34 +321,11 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
alignment: WrapAlignment.end,
|
||||
runAlignment: WrapAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: _goToPage,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
import '../../../../../core/config/environment.dart';
|
||||
import '../../../../../widgets/spec_page.dart';
|
||||
@@ -131,9 +132,6 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -252,41 +250,11 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
'페이지 $currentPage / $totalPages',
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
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
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _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('마지막'),
|
||||
),
|
||||
],
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: (page) => _controller.fetch(page: page),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -386,9 +354,9 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(),
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -418,10 +386,10 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
isActive: isActiveNotifier.value,
|
||||
note: note.isEmpty ? null : note,
|
||||
);
|
||||
final navigator = Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final navigator = Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final response = isEdit
|
||||
? await _controller.update(groupId!, input)
|
||||
: await _controller.create(input);
|
||||
@@ -577,17 +545,13 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
content: Text('"${group.groupName}" 그룹을 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
import '../../../../../core/config/environment.dart';
|
||||
import '../../../../../core/permissions/permission_manager.dart';
|
||||
@@ -182,9 +183,6 @@ class _GroupPermissionEnabledPageState
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -365,41 +363,11 @@ class _GroupPermissionEnabledPageState
|
||||
'페이지 $currentPage / $totalPages',
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
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
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _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('마지막'),
|
||||
),
|
||||
],
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: (page) => _controller.fetch(page: page),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -500,9 +468,9 @@ class _GroupPermissionEnabledPageState
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
import '../../../../../core/config/environment.dart';
|
||||
import '../../../../../widgets/spec_page.dart';
|
||||
@@ -152,9 +153,6 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -308,41 +306,11 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
'페이지 $currentPage / $totalPages',
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
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
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _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('마지막'),
|
||||
),
|
||||
],
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: (page) => _controller.fetch(page: page),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -436,9 +404,9 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -730,10 +698,8 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
secondaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(false),
|
||||
child: const Text('취소'),
|
||||
);
|
||||
},
|
||||
@@ -741,10 +707,8 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
primaryAction: Builder(
|
||||
builder: (dialogContext) {
|
||||
return ShadButton.destructive(
|
||||
onPressed: () => Navigator.of(
|
||||
dialogContext,
|
||||
rootNavigator: true,
|
||||
).pop(true),
|
||||
onPressed: () =>
|
||||
Navigator.of(dialogContext, rootNavigator: true).pop(true),
|
||||
child: const Text('삭제'),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_table.dart';
|
||||
import 'package:superport_v2/widgets/components/responsive_section.dart';
|
||||
|
||||
@@ -157,9 +158,6 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -324,34 +322,11 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
alignment: WrapAlignment.end,
|
||||
runAlignment: WrapAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: _goToPage,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
import '../../../../../core/config/environment.dart';
|
||||
import '../../../../../core/permissions/permission_manager.dart';
|
||||
@@ -164,9 +165,6 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -292,41 +290,11 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
'페이지 $currentPage / $totalPages',
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
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
|
||||
? null
|
||||
: () => _controller.fetch(page: currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _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('마지막'),
|
||||
),
|
||||
],
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: (page) => _controller.fetch(page: page),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -475,10 +443,7 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
secondaryAction: ValueListenableBuilder<bool>(
|
||||
valueListenable: saving,
|
||||
builder: (context, isSaving, _) {
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
);
|
||||
final navigator = Navigator.of(context, rootNavigator: true);
|
||||
return ShadButton.ghost(
|
||||
onPressed: isSaving ? null : () => navigator.pop(false),
|
||||
child: const Text('취소'),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_table.dart';
|
||||
import 'package:superport_v2/widgets/components/responsive_section.dart';
|
||||
|
||||
@@ -150,9 +151,6 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
return AppLayout(
|
||||
title: '제조사(벤더) 관리',
|
||||
@@ -256,34 +254,11 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
alignment: WrapAlignment.end,
|
||||
runAlignment: WrapAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: _goToPage,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_dialog.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_table.dart';
|
||||
import 'package:superport_v2/widgets/components/responsive_section.dart';
|
||||
import 'package:superport_v2/features/util/postal_search/presentation/models/postal_search_result.dart';
|
||||
@@ -159,9 +160,6 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
final showReset =
|
||||
_searchController.text.isNotEmpty ||
|
||||
@@ -268,34 +266,11 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
alignment: WrapAlignment.end,
|
||||
runAlignment: WrapAlignment.end,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(1),
|
||||
child: const Text('처음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || currentPage <= 1
|
||||
? null
|
||||
: () => _goToPage(currentPage - 1),
|
||||
child: const Text('이전'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: _controller.isLoading || !hasNext
|
||||
? null
|
||||
: () => _goToPage(currentPage + 1),
|
||||
child: const Text('다음'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
_controller.isLoading || currentPage >= totalPages
|
||||
? null
|
||||
: () => _goToPage(totalPages),
|
||||
child: const Text('마지막'),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
isBusy: _controller.isLoading,
|
||||
onPageSelected: _goToPage,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
98
lib/widgets/components/superport_pagination_controls.dart
Normal file
98
lib/widgets/components/superport_pagination_controls.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
/// 페이지 이동용 <<, <, >, >> 버튼을 공통 스타일로 제공하는 위젯.
|
||||
class SuperportPaginationControls extends StatelessWidget {
|
||||
const SuperportPaginationControls({
|
||||
super.key,
|
||||
required this.currentPage,
|
||||
required this.totalPages,
|
||||
this.onPageSelected,
|
||||
this.isBusy = false,
|
||||
this.size = ShadButtonSize.sm,
|
||||
this.spacing = 8,
|
||||
this.mainAxisSize = MainAxisSize.min,
|
||||
});
|
||||
|
||||
/// 현재 선택된 페이지 번호(1-base).
|
||||
final int currentPage;
|
||||
|
||||
/// 전체 페이지 수.
|
||||
final int totalPages;
|
||||
|
||||
/// 페이지 변경 콜백. `null`이면 모든 버튼을 비활성화한다.
|
||||
final void Function(int page)? onPageSelected;
|
||||
|
||||
/// 비동기 작업 중 여부. `true`면 버튼이 비활성화된다.
|
||||
final bool isBusy;
|
||||
|
||||
/// 버튼 크기 옵션.
|
||||
final ShadButtonSize size;
|
||||
|
||||
/// 버튼 간 간격.
|
||||
final double spacing;
|
||||
|
||||
/// 내부 Row의 MainAxisSize.
|
||||
final MainAxisSize mainAxisSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final int safeTotal = totalPages <= 0 ? 1 : totalPages;
|
||||
final int safeCurrent = currentPage < 1
|
||||
? 1
|
||||
: (currentPage > safeTotal ? safeTotal : currentPage);
|
||||
final bool canInteract = onPageSelected != null && !isBusy;
|
||||
final bool canGoPrev = canInteract && safeCurrent > 1;
|
||||
final bool canGoNext = canInteract && safeCurrent < safeTotal;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: mainAxisSize,
|
||||
children: _withSpacing([
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronsLeft,
|
||||
enabled: canGoPrev,
|
||||
onTap: () => onPageSelected?.call(1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronLeft,
|
||||
enabled: canGoPrev,
|
||||
onTap: () => onPageSelected?.call(safeCurrent - 1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronRight,
|
||||
enabled: canGoNext,
|
||||
onTap: () => onPageSelected?.call(safeCurrent + 1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronsRight,
|
||||
enabled: canGoNext,
|
||||
onTap: () => onPageSelected?.call(safeTotal),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _withSpacing(List<Widget> children) {
|
||||
final result = <Widget>[];
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (i > 0) {
|
||||
result.add(SizedBox(width: spacing));
|
||||
}
|
||||
result.add(children[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Widget _buildButton({
|
||||
required IconData icon,
|
||||
required bool enabled,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return ShadButton.ghost(
|
||||
size: size,
|
||||
onPressed: enabled ? onTap : null,
|
||||
child: Icon(icon, size: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
/// 테이블 정렬 상태 정보를 보관하는 모델.
|
||||
class SuperportTableSortState {
|
||||
@@ -276,15 +277,14 @@ class _PaginationFooter extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
final int totalPages =
|
||||
pagination.totalPages <= 0 ? 1 : pagination.totalPages;
|
||||
final int totalPages = pagination.totalPages <= 0
|
||||
? 1
|
||||
: pagination.totalPages;
|
||||
final int currentPage = pagination.currentPage < 1
|
||||
? 1
|
||||
: (pagination.currentPage > totalPages
|
||||
? totalPages
|
||||
: pagination.currentPage);
|
||||
final canGoPrev = currentPage > 1 && !isLoading;
|
||||
final canGoNext = currentPage < totalPages && !isLoading;
|
||||
? totalPages
|
||||
: pagination.currentPage);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -316,33 +316,11 @@ class _PaginationFooter extends StatelessWidget {
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoPrev ? () => onPageChange?.call(1) : null,
|
||||
child: const Icon(lucide.LucideIcons.chevronsLeft, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoPrev
|
||||
? () => onPageChange?.call(currentPage - 1)
|
||||
: null,
|
||||
child: const Icon(lucide.LucideIcons.chevronLeft, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoNext
|
||||
? () => onPageChange?.call(currentPage + 1)
|
||||
: null,
|
||||
child: const Icon(lucide.LucideIcons.chevronRight, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
canGoNext ? () => onPageChange?.call(totalPages) : null,
|
||||
child: const Icon(lucide.LucideIcons.chevronsRight, size: 16),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
onPageSelected: onPageChange,
|
||||
isBusy: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user