926 lines
33 KiB
Dart
926 lines
33 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:tugas_akhir_supabase/utils/date_formatter.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/add_schedule_dialog.dart';
|
|
|
|
class ScheduleListScreen extends StatefulWidget {
|
|
const ScheduleListScreen({super.key});
|
|
|
|
@override
|
|
_ScheduleListScreenState createState() => _ScheduleListScreenState();
|
|
}
|
|
|
|
class _ScheduleListScreenState extends State<ScheduleListScreen> {
|
|
bool _isLoading = true;
|
|
List<Map<String, dynamic>> _schedules = [];
|
|
|
|
// Map untuk warna dan ikon tanaman
|
|
final Map<String, Map<String, dynamic>> _cropIcons = {
|
|
'Padi': {'icon': Icons.grass, 'color': Color.fromARGB(255, 6, 75, 9)},
|
|
'Jagung': {'icon': Icons.eco, 'color': Color.fromARGB(255, 188, 171, 16)},
|
|
'Kedelai': {'icon': Icons.spa, 'color': Color(0xFFFFA000)},
|
|
'Cabai': {'icon': Icons.whatshot, 'color': Color(0xFFE53935)},
|
|
'Tomat': {'icon': Icons.circle, 'color': Color(0xFFE53935)},
|
|
'Bawang': {'icon': Icons.layers, 'color': Color(0xFFAB47BC)},
|
|
'Lainnya': {'icon': Icons.local_florist, 'color': Color(0xFF42A5F5)},
|
|
};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_fetchSchedules();
|
|
|
|
// Clear any existing error messages
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
|
ScaffoldMessenger.of(context).clearSnackBars();
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _fetchSchedules() async {
|
|
if (!mounted) return;
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
return;
|
|
}
|
|
|
|
// Perbaiki query dengan relasi yang tepat
|
|
final response = await Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.select('*, fields:fields!crop_schedules_field_id_fkey(*)')
|
|
.eq('user_id', user.id)
|
|
.order('start_date', ascending: false);
|
|
|
|
debugPrint('INFO: Raw schedules response type: ${response.runtimeType}');
|
|
debugPrint('INFO: Raw schedules response: $response');
|
|
|
|
debugPrint('INFO: Response is a List with ${response.length} items');
|
|
|
|
// Jangan lakukan filter tambahan, gunakan semua data yang diterima
|
|
final schedulesList = response.map((item) => item).toList();
|
|
debugPrint('INFO: Parsed schedules count: ${schedulesList.length}');
|
|
|
|
if (schedulesList.isNotEmpty) {
|
|
debugPrint('INFO: First schedule: ${schedulesList.first}');
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_schedules = schedulesList;
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
debugPrint('ERROR: Error fetching schedules: $e');
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(SnackBar(content: Text('Error: ${e.toString()}')));
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8FAFC),
|
|
appBar: AppBar(
|
|
title: Text(
|
|
'Jadwal Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w700,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
backgroundColor: const Color.fromARGB(255, 15, 92, 18),
|
|
foregroundColor: Colors.white,
|
|
elevation: 0,
|
|
flexibleSpace: Container(
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Color.fromARGB(255, 15, 92, 18), Color(0xFF2E7D32)],
|
|
),
|
|
),
|
|
),
|
|
actions: [
|
|
Container(
|
|
margin: const EdgeInsets.only(right: 8),
|
|
child: IconButton(
|
|
icon: Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Icon(Icons.refresh, size: 20),
|
|
),
|
|
tooltip: 'Refresh Data',
|
|
onPressed: () {
|
|
setState(() => _isLoading = true);
|
|
_fetchSchedules();
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
body:
|
|
_isLoading
|
|
? const Center(
|
|
child: CircularProgressIndicator(
|
|
color: Color.fromARGB(255, 15, 92, 18),
|
|
strokeWidth: 3,
|
|
),
|
|
)
|
|
: _schedules.isEmpty
|
|
? _buildEmptyState()
|
|
: _buildScheduleList(),
|
|
floatingActionButton: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(16),
|
|
gradient: const LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Color.fromARGB(255, 15, 92, 18), Color(0xFF2E7D32)],
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color.fromARGB(255, 15, 92, 18).withOpacity(0.4),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 6),
|
|
),
|
|
],
|
|
),
|
|
child: FloatingActionButton(
|
|
onPressed: () {
|
|
_showAddScheduleDialog();
|
|
},
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
child: const Icon(Icons.add, color: Colors.white, size: 28),
|
|
),
|
|
),
|
|
bottomNavigationBar: null,
|
|
);
|
|
}
|
|
|
|
Widget _buildEmptyState() {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(32),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: BoxDecoration(
|
|
color: const Color.fromARGB(255, 15, 92, 18).withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.agriculture,
|
|
size: 64,
|
|
color: const Color.fromARGB(255, 15, 92, 18),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Belum Ada Jadwal Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w700,
|
|
color: const Color(0xFF2E7D32),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
'Mulai perjalanan bertani Anda dengan\nmembuat jadwal tanam pertama',
|
|
textAlign: TextAlign.center,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
height: 1.5,
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
gradient: const LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Color.fromARGB(255, 15, 92, 18), Color(0xFF2E7D32)],
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color.fromARGB(
|
|
255,
|
|
15,
|
|
92,
|
|
18,
|
|
).withOpacity(0.3),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 6),
|
|
),
|
|
],
|
|
),
|
|
child: ElevatedButton.icon(
|
|
onPressed: () {
|
|
_showAddScheduleDialog();
|
|
},
|
|
icon: const Icon(Icons.add, size: 20),
|
|
label: Text(
|
|
'Buat Jadwal Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.transparent,
|
|
foregroundColor: Colors.white,
|
|
elevation: 0,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 16,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildScheduleList() {
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(16),
|
|
itemCount: _schedules.length,
|
|
itemBuilder: (context, index) {
|
|
final schedule = _schedules[index];
|
|
final scheduleId = schedule['id'];
|
|
final cropName = schedule['crop_name'] ?? 'Tanaman';
|
|
final fieldName = schedule['fields']?['name'] ?? 'Lahan';
|
|
final startDate = DateTime.parse(schedule['start_date']);
|
|
final endDate = DateTime.parse(schedule['end_date']);
|
|
final status = schedule['status'] ?? 'active';
|
|
|
|
// Get crop icon and color
|
|
final cropInfo = _cropIcons[cropName] ?? _cropIcons['Lainnya']!;
|
|
final IconData cropIcon = cropInfo['icon'];
|
|
final Color cropColor = cropInfo['color'];
|
|
|
|
// Calculate progress
|
|
final totalDuration = endDate.difference(startDate).inDays;
|
|
final elapsedDuration = DateTime.now().difference(startDate).inDays;
|
|
double progress = elapsedDuration / totalDuration;
|
|
progress = progress.clamp(0.0, 1.0);
|
|
|
|
// Status color now uses crop color
|
|
Color statusColor = cropColor;
|
|
String statusText;
|
|
IconData statusIcon;
|
|
|
|
switch (status.toLowerCase()) {
|
|
case 'active':
|
|
statusText = 'Aktif';
|
|
statusIcon = Icons.play_circle_filled;
|
|
break;
|
|
case 'completed':
|
|
statusText = 'Selesai';
|
|
statusIcon = Icons.check_circle;
|
|
break;
|
|
case 'cancelled':
|
|
statusText = 'Dibatalkan';
|
|
statusIcon = Icons.cancel;
|
|
break;
|
|
default:
|
|
statusText = 'Pending';
|
|
statusIcon = Icons.pause_circle_filled;
|
|
}
|
|
|
|
return Dismissible(
|
|
key: Key(scheduleId),
|
|
background: _buildDismissibleBackground(true),
|
|
secondaryBackground: _buildDismissibleBackground(false),
|
|
confirmDismiss: (direction) async {
|
|
if (direction == DismissDirection.endToStart) {
|
|
return await _showDeleteConfirmationDialog(scheduleId, cropName);
|
|
} else if (direction == DismissDirection.startToEnd) {
|
|
_editSchedule(schedule);
|
|
return false;
|
|
}
|
|
return false;
|
|
},
|
|
onDismissed: (direction) {
|
|
if (direction == DismissDirection.endToStart) {
|
|
setState(() {
|
|
_schedules.removeAt(index);
|
|
});
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Jadwal $cropName telah dihapus'),
|
|
backgroundColor: const Color.fromARGB(255, 15, 92, 18),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: Container(
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.08),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
// Add subtle border with crop color
|
|
border: Border.all(color: cropColor.withOpacity(0.3), width: 1.5),
|
|
),
|
|
child: InkWell(
|
|
onTap: () {
|
|
Navigator.pushNamed(
|
|
context,
|
|
'/kalender-detail',
|
|
arguments: {'scheduleId': schedule['id']},
|
|
);
|
|
},
|
|
borderRadius: BorderRadius.circular(16),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Header Section
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: cropColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Icon(cropIcon, color: cropColor, size: 24),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
cropName,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
color: cropColor,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.location_on,
|
|
size: 16,
|
|
color: Colors.grey[600],
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: Text(
|
|
fieldName,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: statusColor,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: statusColor.withOpacity(0.3),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(statusIcon, size: 14, color: Colors.white),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
statusText,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// Date Range
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: cropColor.withOpacity(0.05),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: cropColor.withOpacity(0.2)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.calendar_today,
|
|
color: cropColor,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'${formatDate(startDate)} - ${formatDate(endDate)}',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: cropColor.withOpacity(0.8),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Progress Section
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Progress Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: cropColor,
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 4,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: cropColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
'${(progress * 100).toInt()}%',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w700,
|
|
color: cropColor,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
// Progress Bar
|
|
Container(
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(4),
|
|
color: Colors.grey.withOpacity(0.1),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(4),
|
|
child: LinearProgressIndicator(
|
|
value: progress,
|
|
backgroundColor: Colors.transparent,
|
|
valueColor: AlwaysStoppedAnimation<Color>(cropColor),
|
|
minHeight: 8,
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Action Buttons
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: OutlinedButton.icon(
|
|
onPressed: () => _editSchedule(schedule),
|
|
icon: const Icon(Icons.edit, size: 16),
|
|
label: Text(
|
|
'Edit',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: cropColor,
|
|
side: BorderSide(color: cropColor),
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
flex: 2,
|
|
child: ElevatedButton.icon(
|
|
onPressed: () {
|
|
Navigator.pushNamed(
|
|
context,
|
|
'/kalender-detail',
|
|
arguments: {'scheduleId': schedule['id']},
|
|
);
|
|
},
|
|
icon: const Icon(Icons.visibility, size: 16),
|
|
label: Text(
|
|
'Lihat Detail',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: cropColor,
|
|
foregroundColor: Colors.white,
|
|
elevation: 2,
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
shadowColor: cropColor.withOpacity(0.3),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildDismissibleBackground(bool isEdit) {
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
decoration: BoxDecoration(
|
|
color: isEdit ? const Color(0xFF2196F3) : const Color(0xFFF44336),
|
|
borderRadius: BorderRadius.circular(16),
|
|
gradient: LinearGradient(
|
|
begin: isEdit ? Alignment.centerLeft : Alignment.centerRight,
|
|
end: isEdit ? Alignment.centerRight : Alignment.centerLeft,
|
|
colors:
|
|
isEdit
|
|
? [const Color(0xFF2196F3), const Color(0xFF1976D2)]
|
|
: [const Color(0xFFF44336), const Color(0xFFD32F2F)],
|
|
),
|
|
),
|
|
alignment: isEdit ? Alignment.centerLeft : Alignment.centerRight,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
isEdit ? Icons.edit : Icons.delete,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
isEdit ? 'Edit' : 'Hapus',
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<bool> _showDeleteConfirmationDialog(
|
|
String scheduleId,
|
|
String cropName,
|
|
) async {
|
|
return await showDialog<bool>(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
backgroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
title: Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF44336).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Icon(
|
|
Icons.warning,
|
|
color: Color(0xFFF44336),
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Text(
|
|
'Hapus Jadwal',
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w700,
|
|
fontSize: 18,
|
|
color: const Color(0xFF2E7D32),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
content: Text(
|
|
'Apakah Anda yakin ingin menghapus jadwal "$cropName"? Semua data terkait juga akan dihapus dan tidak dapat dikembalikan.',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[700],
|
|
height: 1.5,
|
|
),
|
|
),
|
|
actions: <Widget>[
|
|
OutlinedButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: Colors.grey[700],
|
|
side: BorderSide(color: Colors.grey[300]!),
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20,
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Batal',
|
|
style: GoogleFonts.poppins(fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
ElevatedButton(
|
|
onPressed: () async {
|
|
Navigator.of(context).pop(true);
|
|
await _deleteSchedule(scheduleId);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFF44336),
|
|
foregroundColor: Colors.white,
|
|
elevation: 2,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20,
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Hapus',
|
|
style: GoogleFonts.poppins(fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
) ??
|
|
false;
|
|
}
|
|
|
|
Future<void> _deleteSchedule(String scheduleId) async {
|
|
try {
|
|
await Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.delete()
|
|
.eq('id', scheduleId);
|
|
} catch (e) {
|
|
debugPrint('ERROR: Failed to delete schedule: $e');
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Gagal menghapus jadwal. Silakan coba lagi.'),
|
|
backgroundColor: const Color(0xFFF44336),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _editSchedule(Map<String, dynamic> schedule) async {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
debugPrint('ERROR: User is null in _editSchedule');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
final response = await Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.select('id, field_id, plot, start_date, end_date')
|
|
.eq('user_id', user.id)
|
|
.neq('id', schedule['id']);
|
|
|
|
final existingSchedules =
|
|
response is List
|
|
? response.map((item) => item).toList()
|
|
: <Map<String, dynamic>>[];
|
|
|
|
if (!mounted) return;
|
|
|
|
final scheduleToEdit = Map<String, dynamic>.from(schedule);
|
|
|
|
final result = await showDialog(
|
|
context: context,
|
|
builder:
|
|
(context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
insetPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 24,
|
|
),
|
|
child: AddScheduleDialog(
|
|
existingSchedules: existingSchedules,
|
|
scheduleToEdit: scheduleToEdit,
|
|
onScheduleAdded: (updatedSchedule) {
|
|
debugPrint('INFO: Schedule updated: $updatedSchedule');
|
|
_fetchSchedules();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
if (result == true) {
|
|
await _fetchSchedules();
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Jadwal berhasil diperbarui'),
|
|
backgroundColor: const Color.fromARGB(255, 15, 92, 18),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint('ERROR: Error preparing edit schedule dialog: $e');
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Terjadi kesalahan. Silakan coba lagi.'),
|
|
backgroundColor: const Color(0xFFF44336),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _showAddScheduleDialog() async {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
debugPrint('ERROR: User is null in _showAddScheduleDialog');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
debugPrint('INFO: Fetching existing schedules for dialog');
|
|
final response = await Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.select('id, field_id, plot, start_date, end_date')
|
|
.eq('user_id', user.id);
|
|
|
|
debugPrint('INFO: Dialog response type: ${response.runtimeType}');
|
|
debugPrint('INFO: Dialog raw response: $response');
|
|
|
|
final existingSchedules =
|
|
response is List
|
|
? response.map((item) => item).toList()
|
|
: <Map<String, dynamic>>[];
|
|
|
|
debugPrint(
|
|
'INFO: Found ${existingSchedules.length} schedules for dialog',
|
|
);
|
|
|
|
if (!mounted) return;
|
|
|
|
final result = await showDialog(
|
|
context: context,
|
|
builder:
|
|
(context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
insetPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 24,
|
|
),
|
|
child: AddScheduleDialog(
|
|
existingSchedules: existingSchedules,
|
|
onScheduleAdded: (newSchedule) {
|
|
debugPrint('INFO: New schedule added: $newSchedule');
|
|
_fetchSchedules();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
if (result == true) {
|
|
debugPrint('INFO: Refreshing schedules after dialog closed');
|
|
await _fetchSchedules();
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error preparing add schedule dialog: $e');
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Terjadi kesalahan. Silakan coba lagi.'),
|
|
backgroundColor: const Color(0xFFF44336),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|