Files
timemaster-kiosk/lib/services/kiosk_service.dart
T
patrickandClaude Sonnet 5 e42cae74f3 feat: TimeMaster Kiosk Grundgerüst (Login + Stempeln)
Android-Tablet-Kiosk-App, gedacht für Betrieb hinter WireGuard-VPN.
Bearer-Token-Geräte-Auth statt Ed25519-Request-Signing (siehe Backend-
Commit f4ff11a im Haupt-Repo). Ersteinrichtung (Server-URL+Token), PIN/
NFC/Listen-Login, Stempeln Ein/Aus/Pause, Heartbeat-Loop.

Bekannte Einschränkung: Android-NFC (13,56 MHz) ist inkompatibel zu den
125-kHz-EM4100-Karten der bestehenden PC-Terminals – separate Karten nötig,
siehe README.

Vollständiger Plan: /home/sysops/.claude/plans/staged-marinating-dove.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dzdm3EWeNdYFUuXFRJryA1
2026-09-10 02:23:59 +02:00

63 lines
2.4 KiB
Dart

import '../models/kiosk_session.dart';
import 'api_client.dart';
/// Bündelt alle /kiosk/*-Aufrufe. Device-Auth (Bearer-Token) hängt der
/// Dio-Interceptor in ApiClient automatisch an; session_token (Mitarbeiter-
/// Login) wird pro Aufruf im Body mitgegeben, siehe
/// backend/app/schemas/kiosk_auth.py.
class KioskService {
final _dio = ApiClient.instance.dio;
Future<KioskSession> loginPin(String personnelNumber, String pin) async {
final r = await _dio.post('/kiosk/auth/pin', data: {
'personnel_number': personnelNumber,
'pin': pin,
});
return KioskSession.fromJson(r.data as Map<String, dynamic>);
}
Future<KioskSession> loginNfc(String nfcUid, String pin) async {
final r = await _dio.post('/kiosk/auth/nfc', data: {
'nfc_uid': nfcUid,
'pin': pin,
});
return KioskSession.fromJson(r.data as Map<String, dynamic>);
}
Future<KioskSession> loginList(String userId) async {
final r = await _dio.post('/kiosk/auth/list', data: {'user_id': userId});
return KioskSession.fromJson(r.data as Map<String, dynamic>);
}
Future<List<KioskEmployee>> listEmployees() async {
final r = await _dio.get('/kiosk/auth/users');
return KioskEmployee.listFromResponse(r.data as Map<String, dynamic>);
}
Future<void> logout(String sessionToken) async {
await _dio.post('/kiosk/auth/logout', data: {'session_token': sessionToken});
}
Future<StampStatus> status(String sessionToken) async {
final r = await _dio.post('/kiosk/stamp/status', data: {'session_token': sessionToken});
return StampStatus.fromJson(r.data as Map<String, dynamic>);
}
Future<StampResult> stampIn(String sessionToken) => _stamp('in', sessionToken);
Future<StampResult> stampOut(String sessionToken) => _stamp('out', sessionToken);
Future<StampResult> breakStart(String sessionToken) => _stamp('break-start', sessionToken);
Future<StampResult> breakEnd(String sessionToken) => _stamp('break-end', sessionToken);
Future<StampResult> _stamp(String action, String sessionToken) async {
final r = await _dio.post('/kiosk/stamp/$action', data: {'session_token': sessionToken});
return StampResult.fromJson(r.data as Map<String, dynamic>);
}
Future<void> heartbeat({int queuedOfflineEntries = 0, required String clientVersion}) async {
await _dio.post('/kiosk/heartbeat', data: {
'queued_offline_entries': queuedOfflineEntries,
'client_version': clientVersion,
});
}
}