FarisaRahmaSari_E31222327/BBS/lib/view/presensi/clockin_view.dart

315 lines
9.2 KiB
Dart

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<ClockinView> {
File? _image;
Position? _currentPosition;
bool _isLoading = false;
String _errorMessage = '';
final TextEditingController _descriptionController = TextEditingController();
@override
void initState() {
super.initState();
_getCurrentLocation();
}
Future<void> _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<void> _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<void> _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,
),
),
],
),
),
);
}
}