feat(frontend): 승인 템플릿 API 통합 및 디버그 로그인 확장
- docs 폴더 문서를 최신 API 계약으로 갱신하고 가이드를 다듬었다\n- approvals data/presentation 레이어를 API v4 스펙에 맞춰 리팩터링했다\n- approver 자동완성 위젯을 신규 공유 레포지토리에 맞춰 교체하고 UX를 보강했다\n- inventory/rental 페이지 테이블 초기화 시 승인 기준 연동을 정비했다\n- 로그인 페이지 디버그 버튼을 tera/exa 계정으로 분리해 QA 로그인을 단순화했다\n- get_it 등록과 테스트 케이스를 신규 공유 리포지토리에 맞춰 업데이트했다
This commit is contained in:
@@ -856,31 +856,36 @@ class _PermissionTable extends StatelessWidget {
|
||||
|
||||
cells.add(
|
||||
ShadTableCell(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onEdit == null ? null : () => onEdit!(permission),
|
||||
child: const Icon(LucideIcons.pencil, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
permission.isDeleted
|
||||
? ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onRestore == null
|
||||
? null
|
||||
: () => onRestore!(permission),
|
||||
child: const Icon(LucideIcons.history, size: 16),
|
||||
)
|
||||
: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onDelete == null
|
||||
? null
|
||||
: () => onDelete!(permission),
|
||||
child: const Icon(LucideIcons.trash2, size: 16),
|
||||
),
|
||||
],
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 6,
|
||||
alignment: WrapAlignment.end,
|
||||
runAlignment: WrapAlignment.end,
|
||||
children: [
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onEdit == null ? null : () => onEdit!(permission),
|
||||
child: const Icon(LucideIcons.pencil, size: 16),
|
||||
),
|
||||
permission.isDeleted
|
||||
? ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onRestore == null
|
||||
? null
|
||||
: () => onRestore!(permission),
|
||||
child: const Icon(LucideIcons.history, size: 16),
|
||||
)
|
||||
: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onDelete == null
|
||||
? null
|
||||
: () => onDelete!(permission),
|
||||
child: const Icon(LucideIcons.trash2, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -42,6 +42,7 @@ class ProductController extends ChangeNotifier {
|
||||
|
||||
List<Vendor> _vendorOptions = const [];
|
||||
List<Uom> _uomOptions = const [];
|
||||
bool _isDisposed = false;
|
||||
|
||||
PaginatedResult<Product>? get result => _result;
|
||||
bool get isLoading => _isLoading;
|
||||
@@ -60,7 +61,7 @@ class ProductController extends ChangeNotifier {
|
||||
Future<void> fetch({int page = 1}) async {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
try {
|
||||
final previous = _result;
|
||||
final int resolvedPage;
|
||||
@@ -95,14 +96,14 @@ class ProductController extends ChangeNotifier {
|
||||
_errorMessage = failure.describe();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
}
|
||||
|
||||
/// 필터/폼에서 사용할 공급업체와 단위 목록을 로드한다.
|
||||
Future<void> loadLookups() async {
|
||||
_isLoadingLookups = true;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
try {
|
||||
debugPrint('[ProductController] 드롭다운 데이터 조회 시작');
|
||||
final vendors = await fetchAllPaginatedItems<Vendor>(
|
||||
@@ -127,7 +128,7 @@ class ProductController extends ChangeNotifier {
|
||||
);
|
||||
} finally {
|
||||
_isLoadingLookups = false;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +138,7 @@ class ProductController extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_query = value;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 공급업체 필터를 변경한다.
|
||||
@@ -146,7 +147,7 @@ class ProductController extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_vendorFilter = vendorId;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 단위(UOM) 필터를 변경한다.
|
||||
@@ -155,7 +156,7 @@ class ProductController extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_uomFilter = uomId;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 사용 여부 필터를 변경한다.
|
||||
@@ -164,7 +165,7 @@ class ProductController extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_statusFilter = filter;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 페이지 크기를 변경한다.
|
||||
@@ -173,7 +174,7 @@ class ProductController extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_pageSize = size;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 제품을 생성한다.
|
||||
@@ -186,7 +187,7 @@ class ProductController extends ChangeNotifier {
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
return null;
|
||||
} finally {
|
||||
_setSubmitting(false);
|
||||
@@ -203,7 +204,7 @@ class ProductController extends ChangeNotifier {
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
return null;
|
||||
} finally {
|
||||
_setSubmitting(false);
|
||||
@@ -220,7 +221,7 @@ class ProductController extends ChangeNotifier {
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
return false;
|
||||
} finally {
|
||||
_setSubmitting(false);
|
||||
@@ -237,7 +238,7 @@ class ProductController extends ChangeNotifier {
|
||||
} catch (error) {
|
||||
final failure = Failure.from(error);
|
||||
_errorMessage = failure.describe();
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
return null;
|
||||
} finally {
|
||||
_setSubmitting(false);
|
||||
@@ -247,12 +248,26 @@ class ProductController extends ChangeNotifier {
|
||||
/// 에러 메시지를 초기화한다.
|
||||
void clearError() {
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// 제출 상태 플래그를 갱신하고 리스너에 알린다.
|
||||
void _setSubmitting(bool value) {
|
||||
_isSubmitting = value;
|
||||
notifyListeners();
|
||||
_notifySafely();
|
||||
}
|
||||
|
||||
/// dispose 이후에는 알림을 중단하여 디버그 에러를 방지한다.
|
||||
void _notifySafely() {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
super.notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user