import 'dart:convert'; import 'dart:io'; import 'package:absen/config/config.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; class CheckinPhotoScreen extends StatefulWidget { final String token; final Position position; const CheckinPhotoScreen({ Key? key, required this.token, required this.position, }) : super(key: key); @override State createState() => _CheckinPhotoScreenState(); } class _CheckinPhotoScreenState extends State { File? _photo; Uint8List? _webPhotoBytes; String? _webPhotoName; bool _isLoading = false; Future _pickPhoto() async { try { if (kIsWeb) { final result = await FilePicker.platform.pickFiles( type: FileType.image, ); if (result != null && result.files.single.bytes != null) { setState(() { _webPhotoBytes = result.files.single.bytes; _webPhotoName = result.files.single.name; }); } } else { final picker = ImagePicker(); final picked = await picker.pickImage( source: ImageSource.camera, imageQuality: 70, ); if (picked != null) { setState(() { _photo = File(picked.path); }); } } } catch (e) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Gagal mengambil foto: $e'))); } } bool _isPhotoReady() { if (kIsWeb) { return _webPhotoBytes != null && _webPhotoName != null; } else { return _photo != null; } } Future _submitCheckin() async { if (!_isPhotoReady()) return; setState(() => _isLoading = true); final token = widget.token; final uri = Uri.parse('${AppConfig.baseUrl}/api/employee/attendance'); final request = http.MultipartRequest('POST', uri) ..headers['Authorization'] = 'Bearer $token' ..fields['type'] = 'in' ..fields['latitude'] = widget.position.latitude.toString() ..fields['longitude'] = widget.position.longitude.toString(); if (kIsWeb && _webPhotoBytes != null && _webPhotoName != null) { request.files.add( http.MultipartFile.fromBytes( 'photo', _webPhotoBytes!, filename: _webPhotoName, ), ); } else if (_photo != null) { request.files.add( await http.MultipartFile.fromPath('photo', _photo!.path), ); } try { final streamed = await request.send(); final response = await http.Response.fromStream(streamed); setState(() => _isLoading = false); final data = jsonDecode(response.body); if (response.statusCode == 200) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(data['message'] ?? 'Check-in berhasil!')), ); Navigator.popUntil(context, (route) => route.isFirst); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(data['message'] ?? 'Check-in gagal!')), ); } } catch (e) { setState(() => _isLoading = false); ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('Terjadi kesalahan.'))); } } @override Widget build(BuildContext context) { final buttonStyle = ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(48), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ); return Scaffold( appBar: AppBar( title: const Text('Check-in: Ambil Foto'), centerTitle: true, ), body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( children: [ const SizedBox(height: 24), const Text( 'Pastikan wajah terlihat jelas, bebas dari objek yang menutupi, dan latar belakang cukup terang.', textAlign: TextAlign.center, style: TextStyle(fontSize: 16), ), const SizedBox(height: 24), ElevatedButton.icon( style: buttonStyle, onPressed: _pickPhoto, icon: const Icon(Icons.camera_alt), label: Text(!_isPhotoReady() ? 'Ambil Foto' : 'Ubah Foto'), ), const SizedBox(height: 16), Expanded( child: Center( child: _isPhotoReady() ? Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black26, blurRadius: 6, offset: Offset(0, 3), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: kIsWeb ? Image.memory( _webPhotoBytes!, height: 250, fit: BoxFit.cover, ) : Image.file( _photo!, height: 250, fit: BoxFit.cover, ), ), ) : Column( mainAxisSize: MainAxisSize.min, children: const [ Icon( Icons.photo_camera_outlined, size: 80, color: Colors.grey, ), SizedBox(height: 8), Text( 'Belum ada foto', style: TextStyle(color: Colors.grey), ), ], ), ), ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( style: buttonStyle, onPressed: _isPhotoReady() && !_isLoading ? _submitCheckin : null, child: _isLoading ? const SizedBox( height: 24, width: 24, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text('Check-in'), ), ), const SizedBox(height: 16), ], ), ), ), ); } }