SIPDAM/samooflutter/lib/api/AbsensiApi.dart

363 lines
13 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class AbsensiApi {
static const String baseUrl = 'https://ta.myhost.id/E31230906/api';
static const String absenMasukEndpoint = '/absensi/absen-masuk';
static const String absenKeluarEndpoint = '/absensi/absen-keluar';
static const String checkStatusEndpoint = '/absensi/check-status';
static const String riwayatEndpoint = '/absensi/riwayat';
static const String kalenderEndpoint = '/absensi/kalender';
static const String rekapEndpoint = '/absensi/rekap';
static const Duration timeoutDuration = Duration(seconds: 30);
Future<String?> _getToken() async {
try {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('access_token');
} catch (e) {
print('Error getting token: $e');
return null;
}
}
Future<Map<String, String>> _getMultipartHeaders() async {
final token = await _getToken();
return {
'Accept': 'application/json',
if (token != null) 'Authorization': 'Bearer $token',
};
}
Future<Map<String, String>> _getHeaders() async {
final token = await _getToken();
return {
'Content-Type': 'application/json',
'Accept': 'application/json',
if (token != null) 'Authorization': 'Bearer $token',
};
}
/// Absen masuk dengan support Web & Mobile
Future<Map<String, dynamic>> absenMasuk({
required int idTeknisi,
XFile? fotoAbsenMasuk,
String? status,
String? keterangan,
double? latitude,
double? longitude,
}) async {
try {
print('=== ABSEN MASUK REQUEST ===');
print('ID Teknisi: $idTeknisi');
print('Status: ${status ?? "null"}');
print('Keterangan: ${keterangan ?? "null"}');
print('Foto: ${fotoAbsenMasuk != null ? "Ada (${fotoAbsenMasuk.name})" : "TIDAK ADA"}');
var request = http.MultipartRequest(
'POST',
Uri.parse('$baseUrl$absenMasukEndpoint'),
);
request.headers.addAll(await _getMultipartHeaders());
request.fields['id_teknisi'] = idTeknisi.toString();
if (status != null && status.isNotEmpty) {
request.fields['status'] = status;
}
if (keterangan != null && keterangan.isNotEmpty) {
request.fields['keterangan'] = keterangan;
}
if (latitude != null) {
request.fields['latitude'] = latitude.toString();
}
if (longitude != null) {
request.fields['longitude'] = longitude.toString();
}
if (fotoAbsenMasuk != null) {
if (kIsWeb) {
final bytes = await fotoAbsenMasuk.readAsBytes();
print('📸 Foto size (Web): ${bytes.length} bytes');
request.files.add(http.MultipartFile.fromBytes(
'foto_absen_masuk', bytes,
filename: fotoAbsenMasuk.name,
));
} else {
print('📸 Foto path (Mobile): ${fotoAbsenMasuk.path}');
request.files.add(await http.MultipartFile.fromPath(
'foto_absen_masuk', fotoAbsenMasuk.path,
));
}
}
print('📤 Sending request to: $baseUrl$absenMasukEndpoint');
final streamedResponse = await request.send().timeout(timeoutDuration);
final response = await http.Response.fromStream(streamedResponse);
print('=== ABSEN MASUK RESPONSE ===');
print('Status Code: ${response.statusCode}');
print('Response Body: ${response.body}');
final data = jsonDecode(response.body);
if (response.statusCode == 201 && data['success'] == true) {
print('✅ ABSEN MASUK SUKSES!');
return {'success': true, 'message': data['message'], 'data': data['data']};
} else if (response.statusCode == 400) {
print('❌ Error 400: ${data['message']}');
return {'success': false,
'message': data['message'] ?? 'Anda sudah melakukan absen masuk hari ini'};
} else if (response.statusCode == 422) {
print('❌ Validation Error: ${data['errors']}');
String errorMessage = 'Validasi gagal';
if (data['errors'] != null) {
final errors = data['errors'] as Map<String, dynamic>;
errorMessage = errors.values.first[0];
}
return {'success': false, 'message': errorMessage, 'errors': data['errors']};
} else {
print('❌ Unknown Error: ${response.statusCode}');
return {'success': false,
'message': data['message'] ?? 'Gagal melakukan absen masuk'};
}
} catch (e, stackTrace) {
print('❌ EXCEPTION di absenMasuk: $e');
print(stackTrace);
return {'success': false, 'message': 'Terjadi kesalahan: ${e.toString()}'};
}
}
/// Absen keluar dengan support Web & Mobile
Future<Map<String, dynamic>> absenKeluar({
required int idTeknisi,
XFile? fotoAbsenKeluar,
String? status,
String? keterangan,
double? latitude,
double? longitude,
}) async {
try {
print('=== ABSEN KELUAR REQUEST ===');
print('ID Teknisi: $idTeknisi');
print('Status: ${status ?? "null"}');
print('Keterangan: ${keterangan ?? "null"}');
print('Foto: ${fotoAbsenKeluar != null ? "Ada (${fotoAbsenKeluar.name})" : "TIDAK ADA"}');
var request = http.MultipartRequest(
'POST',
Uri.parse('$baseUrl$absenKeluarEndpoint'),
);
request.headers.addAll(await _getMultipartHeaders());
request.fields['id_teknisi'] = idTeknisi.toString();
if (status != null && status.isNotEmpty) {
request.fields['status'] = status;
}
if (keterangan != null && keterangan.isNotEmpty) {
request.fields['keterangan'] = keterangan;
}
if (latitude != null) {
request.fields['latitude'] = latitude.toString();
}
if (longitude != null) {
request.fields['longitude'] = longitude.toString();
}
if (fotoAbsenKeluar != null) {
if (kIsWeb) {
final bytes = await fotoAbsenKeluar.readAsBytes();
print('📸 Foto size (Web): ${bytes.length} bytes');
request.files.add(http.MultipartFile.fromBytes(
'foto_absen_keluar', bytes,
filename: fotoAbsenKeluar.name,
));
} else {
print('📸 Foto path (Mobile): ${fotoAbsenKeluar.path}');
request.files.add(await http.MultipartFile.fromPath(
'foto_absen_keluar', fotoAbsenKeluar.path,
));
}
}
print('📤 Sending request to: $baseUrl$absenKeluarEndpoint');
final streamedResponse = await request.send().timeout(timeoutDuration);
final response = await http.Response.fromStream(streamedResponse);
print('=== ABSEN KELUAR RESPONSE ===');
print('Status Code: ${response.statusCode}');
print('Response Body: ${response.body}');
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['success'] == true) {
print('✅ ABSEN KELUAR SUKSES!');
return {'success': true, 'message': data['message'], 'data': data['data']};
} else if (response.statusCode == 400) {
print('❌ Error 400: ${data['message']}');
return {'success': false,
'message': data['message'] ?? 'Belum melakukan absen masuk atau sudah absen keluar'};
} else if (response.statusCode == 422) {
print('❌ Validation Error: ${data['errors']}');
String errorMessage = 'Validasi gagal';
if (data['errors'] != null) {
final errors = data['errors'] as Map<String, dynamic>;
errorMessage = errors.values.first[0];
}
return {'success': false, 'message': errorMessage, 'errors': data['errors']};
} else {
print('❌ Unknown Error: ${response.statusCode}');
return {'success': false,
'message': data['message'] ?? 'Gagal melakukan absen keluar'};
}
} catch (e, stackTrace) {
print('❌ EXCEPTION di absenKeluar: $e');
print(stackTrace);
return {'success': false, 'message': 'Terjadi kesalahan: ${e.toString()}'};
}
}
/// Cek status absensi hari ini
Future<Map<String, dynamic>> checkStatus(int idTeknisi) async {
try {
final token = await _getToken();
final response = await http.get(
Uri.parse('$baseUrl$checkStatusEndpoint/$idTeknisi'),
headers: {
'Accept': 'application/json',
if (token != null) 'Authorization': 'Bearer $token',
},
).timeout(timeoutDuration);
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['success'] == true) {
return {'success': true, 'message': data['message'], 'data': data['data']};
} else if (response.statusCode == 404) {
return {'success': false, 'message': 'Teknisi tidak ditemukan'};
} else {
return {'success': false,
'message': data['message'] ?? 'Gagal mengecek status absensi'};
}
} catch (e) {
return {'success': false, 'message': 'Terjadi kesalahan: ${e.toString()}'};
}
}
// ── Method baru ────────────────────────────────────────────────────────────
/// Ambil riwayat absensi per bulan
/// Endpoint: GET /api/absensi/riwayat?id_teknisi=1&bulan=3&tahun=2026
Future<Map<String, dynamic>> getRiwayat({
required int idTeknisi,
required int bulan,
required int tahun,
}) async {
try {
final url = Uri.parse(
'$baseUrl$riwayatEndpoint?id_teknisi=$idTeknisi&bulan=$bulan&tahun=$tahun');
print('📋 GET Riwayat: $url');
final response = await http.get(url,
headers: await _getHeaders()).timeout(timeoutDuration);
print('Riwayat status: ${response.statusCode}');
print('Riwayat body: ${response.body}');
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['success'] == true) {
return {'success': true, 'data': data['data'] ?? []};
}
return {
'success': false,
'message': data['message'] ?? 'Gagal memuat riwayat',
'data': [],
};
} catch (e) {
print('❌ getRiwayat error: $e');
return {'success': false, 'message': e.toString(), 'data': []};
}
}
/// Ambil status absensi per tanggal dalam 1 bulan (untuk kalender)
/// Endpoint: GET /api/absensi/kalender?id_teknisi=1&bulan=3&tahun=2026
/// Response data: { "1": "hadir", "5": "izin", "10": "alpha", ... }
Future<Map<String, dynamic>> getKalender({
required int idTeknisi,
required int bulan,
required int tahun,
}) async {
try {
final url = Uri.parse(
'$baseUrl$kalenderEndpoint?id_teknisi=$idTeknisi&bulan=$bulan&tahun=$tahun');
print('📅 GET Kalender: $url');
final response = await http.get(url,
headers: await _getHeaders()).timeout(timeoutDuration);
print('Kalender status: ${response.statusCode}');
print('Kalender body: ${response.body}');
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['success'] == true) {
return {'success': true, 'data': data['data'] ?? {}};
}
return {
'success': false,
'message': data['message'] ?? 'Gagal memuat kalender',
'data': {},
};
} catch (e) {
print('❌ getKalender error: $e');
return {'success': false, 'message': e.toString(), 'data': {}};
}
}
/// Ambil rekap absensi bulanan
/// Endpoint: GET /api/absensi/rekap?id_teknisi=1&bulan=3&tahun=2026
/// Response data: { "bulan": "Maret 2026", "hadir": 14, "izin": 1, ... }
Future<Map<String, dynamic>> getRekap({
required int idTeknisi,
required int bulan,
required int tahun,
}) async {
try {
final url = Uri.parse(
'$baseUrl$rekapEndpoint?id_teknisi=$idTeknisi&bulan=$bulan&tahun=$tahun');
print('📊 GET Rekap: $url');
final response = await http.get(url,
headers: await _getHeaders()).timeout(timeoutDuration);
print('Rekap status: ${response.statusCode}');
print('Rekap body: ${response.body}');
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['success'] == true) {
return {'success': true, 'data': data['data'] ?? {}};
}
return {
'success': false,
'message': data['message'] ?? 'Gagal memuat rekap',
'data': {},
};
} catch (e) {
print('❌ getRekap error: $e');
return {'success': false, 'message': e.toString(), 'data': {}};
}
}
}