import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:geolocator/geolocator.dart'; import 'dart:io'; import 'package:http/http.dart' as http; import 'package:qyuota/config/api_config.dart'; import 'package:qyuota/services/auth_service.dart'; import 'package:intl/intl.dart'; class ClockinView extends StatefulWidget { @override _ClockinViewState createState() => _ClockinViewState(); } class _ClockinViewState extends State { File? _image; Position? _currentPosition; bool _isLoading = false; String _errorMessage = ''; final TextEditingController _descriptionController = TextEditingController(); @override void initState() { super.initState(); _getCurrentLocation(); } Future _getCurrentLocation() async { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { setState(() { _errorMessage = 'Layanan lokasi tidak aktif. Mohon aktifkan GPS Anda.'; }); return; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { setState(() { _errorMessage = 'Izin lokasi ditolak'; }); return; } } if (permission == LocationPermission.deniedForever) { setState(() { _errorMessage = 'Izin lokasi ditolak secara permanen. Mohon ubah di pengaturan.'; }); return; } Position position = await Geolocator.getCurrentPosition(); setState(() { _currentPosition = position; _errorMessage = ''; }); } catch (e) { setState(() { _errorMessage = 'Error mendapatkan lokasi: $e'; }); } } Future _getImage() async { try { final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.camera); if (image != null) { setState(() { _image = File(image.path); }); } } catch (e) { setState(() { _errorMessage = 'Error mengambil foto: $e'; }); } } Future _submitPresensi() async { if (_image == null) { setState(() { _errorMessage = 'Mohon ambil foto terlebih dahulu'; }); return; } if (_currentPosition == null) { setState(() { _errorMessage = 'Mohon tunggu, sedang mendapatkan lokasi'; }); return; } setState(() { _isLoading = true; _errorMessage = ''; }); try { final token = await AuthService().getToken(); var request = http.MultipartRequest( 'POST', Uri.parse('${ApiConfig.baseUrl}/api/mobile/presensi'), ); // Tambahkan headers request.headers.addAll({ 'Authorization': 'Bearer $token', 'Accept': 'application/json', }); // Tambahkan fields request.fields.addAll({ 'status': 'Hadir', 'description': _descriptionController.text, 'latitude': _currentPosition!.latitude.toString(), 'longitude': _currentPosition!.longitude.toString(), 'clock_type': 'in', }); // Tambahkan file foto request.files.add( await http.MultipartFile.fromPath( 'photo', _image!.path, ), ); var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); if (response.statusCode == 200) { Get.back(); Get.snackbar( 'Sukses', 'Presensi berhasil disimpan', backgroundColor: Colors.green, colorText: Colors.white, ); } else { setState(() { _errorMessage = 'Gagal menyimpan presensi: ${response.body}'; }); } } catch (e) { setState(() { _errorMessage = 'Error: $e'; }); } finally { setState(() { _isLoading = false; }); } } @override Widget build(BuildContext context) { // Get current time String currentTime = DateFormat('HH:mm').format(DateTime.now()); bool isLate = DateFormat('HH:mm').parse(currentTime).isAfter( DateFormat('HH:mm').parse('08:00') ); return Scaffold( appBar: AppBar( title: Text('Clock In'), backgroundColor: Colors.blue, ), body: SingleChildScrollView( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Current Time Display Card( child: Padding( padding: EdgeInsets.all(16), child: Column( children: [ Text( currentTime, style: TextStyle( fontSize: 36, fontWeight: FontWeight.bold, color: isLate ? Colors.red : Colors.green, ), ), SizedBox(height: 8), Text( isLate ? 'Anda Terlambat!' : 'Tepat Waktu', style: TextStyle( color: isLate ? Colors.red : Colors.green, fontWeight: FontWeight.bold, ), ), ], ), ), ), SizedBox(height: 16), // Location Status Card( child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Lokasi:', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), if (_currentPosition != null) Text('Lat: ${_currentPosition!.latitude}\nLong: ${_currentPosition!.longitude}') else if (_errorMessage.isNotEmpty) Text(_errorMessage, style: TextStyle(color: Colors.red)) else Text('Mendapatkan lokasi...'), ], ), ), ), SizedBox(height: 16), // Photo Section Card( child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Foto:', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), if (_image != null) Image.file( _image!, height: 200, fit: BoxFit.cover, ) else Container( height: 200, color: Colors.grey[200], child: Icon(Icons.camera_alt, size: 50, color: Colors.grey), ), SizedBox(height: 8), ElevatedButton.icon( onPressed: _getImage, icon: Icon(Icons.camera_alt), label: Text('Ambil Foto'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, padding: EdgeInsets.symmetric(vertical: 12), ), ), ], ), ), ), SizedBox(height: 16), // Description Field Card( child: Padding( padding: EdgeInsets.all(16), child: TextField( controller: _descriptionController, decoration: InputDecoration( labelText: 'Keterangan (opsional)', border: OutlineInputBorder(), ), maxLines: 3, ), ), ), SizedBox(height: 24), // Submit Button ElevatedButton( onPressed: _isLoading ? null : _submitPresensi, child: _isLoading ? CircularProgressIndicator(color: Colors.white) : Text('Submit Presensi'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, padding: EdgeInsets.symmetric(vertical: 16), ), ), if (_errorMessage.isNotEmpty) Padding( padding: EdgeInsets.only(top: 16), child: Text( _errorMessage, style: TextStyle(color: Colors.red), textAlign: TextAlign.center, ), ), ], ), ), ); } }