2 Commits
Author SHA1 Message Date
sihoo 3a62ebacd8 출석/무단반출 팝업 중복 표시 버그 수정 + 앱 버전 1.1.2
- 새 팝업(출석완료/무단반출)을 띄우기 전 이전에 열려있던 팝업을 먼저 닫도록 수정
- 재태깅이 짧은 간격으로 반복되면 팝업이 여러 개 겹쳐 쌓이던 문제 해결
2026-08-04 14:24:35 +09:00
sihoo 26fb4c32cf 테스트용 허용시간 초기화 디버그 버튼 추가 2026-08-04 14:15:20 +09:00
3 changed files with 70 additions and 3 deletions
+16 -2
View File
@@ -34,6 +34,7 @@ class _NfcPocketCheckInScreenState extends State<NfcPocketCheckInScreen> {
String? _activePocketNumber;
StreamSubscription<Map<String, dynamic>?>? _violationSub;
bool _isDialogOpen = false; // 출석/위반 팝업이 겹쳐서 뜨는 것을 막기 위한 플래그
bool get _watchServiceSupported => !kIsWeb && Platform.isAndroid;
@@ -211,7 +212,18 @@ class _NfcPocketCheckInScreenState extends State<NfcPocketCheckInScreen> {
_showSnackBar("✅ 감시가 종료되었습니다. 수고하셨습니다!", Colors.green);
}
/// 이미 떠 있는 팝업(출석 완료/무단반출 감지)이 있으면 새 팝업을 띄우기 전에 먼저 닫는다.
/// (태깅→반출→재태깅이 빠르게 반복되면 팝업이 여러 개 겹쳐 쌓이는 것을 방지)
void _closeAnyOpenDialog() {
if (_isDialogOpen && mounted) {
Navigator.of(context, rootNavigator: true).pop();
}
_isDialogOpen = false;
}
void _showViolationDialog(String? pocketNumber) {
_closeAnyOpenDialog();
_isDialogOpen = true;
showDialog(
context: context,
builder: (context) => AlertDialog(
@@ -230,11 +242,13 @@ class _NfcPocketCheckInScreenState extends State<NfcPocketCheckInScreen> {
),
],
),
);
).then((_) => _isDialogOpen = false);
}
/// 🎊 출석 성공 알림창
void _showSuccessDialog(String pocketNumber) {
_closeAnyOpenDialog();
_isDialogOpen = true;
showDialog(
context: context,
builder: (context) => AlertDialog(
@@ -249,7 +263,7 @@ class _NfcPocketCheckInScreenState extends State<NfcPocketCheckInScreen> {
),
],
),
);
).then((_) => _isDialogOpen = false);
}
void _showSnackBar(String text, Color color) {
+53
View File
@@ -211,6 +211,54 @@ class _TeacherAttendancePageState extends State<TeacherAttendancePage> {
_setAttendanceTime(formatted);
}
/// 🧪 테스트용: 하교(12시간)/반출 허용 시간 설정 등으로 켜져 있는 허용 시간대를 즉시 해제한다.
Future<void> _resetTestPermissions() async {
try {
final response = await http.post(Uri.parse('$baseUrl/api/debug/reset-permissions'));
final result = jsonDecode(utf8.decode(response.bodyBytes));
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(result['message'] ?? '처리되었습니다.')),
);
_fetchAll();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('❌ 초기화 실패: $e')),
);
}
}
void _showTestResetConfirmDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('🧪 테스트용 허용시간 초기화'),
content: const Text(
'하교 처리나 반출 허용 시간 설정으로 켜져 있는 모든 허용 시간대를 지금 즉시 해제합니다.\n'
'(무단반출 감지 테스트할 때만 사용하세요)',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('취소'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_resetTestPermissions();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[700],
foregroundColor: Colors.white,
),
child: const Text('초기화'),
),
],
),
);
}
void _showAllowDialog(String studentId, String studentName) {
final controller = TextEditingController(text: "5");
showDialog(
@@ -445,6 +493,11 @@ class _TeacherAttendancePageState extends State<TeacherAttendancePage> {
style: TextStyle(color: Colors.white),
),
),
IconButton(
onPressed: _showTestResetConfirmDialog,
icon: const Icon(Icons.bug_report_outlined, color: Colors.white70),
tooltip: '🧪 테스트용: 허용시간 초기화',
),
const SizedBox(width: 8),
],
),
+1 -1
View File
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.1.1+2
version: 1.1.2+3
environment:
sdk: ^3.12.2