733 lines
22 KiB
Dart
733 lines
22 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:table_calendar/table_calendar.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:tugas_akhir_supabase/utils/date_formatter.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/add_schedule_dialog.dart';
|
|
import 'dart:async';
|
|
import 'package:tugas_akhir_supabase/utils/app_events.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/schedule_list_screen.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/field_management_screen.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/add_field_bottom_sheet.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
class KalenderTanamScreen extends StatefulWidget {
|
|
const KalenderTanamScreen({super.key});
|
|
|
|
@override
|
|
_KalenderTanamScreenState createState() => _KalenderTanamScreenState();
|
|
}
|
|
|
|
class _KalenderTanamScreenState extends State<KalenderTanamScreen> {
|
|
CalendarFormat _calendarFormat = CalendarFormat.month;
|
|
DateTime _focusedDay = DateTime.now();
|
|
DateTime? _selectedDay;
|
|
Map<DateTime, List<dynamic>> _events = {};
|
|
bool _isLoading = true;
|
|
int _activeSchedules = 0;
|
|
int _totalFields = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_selectedDay = _focusedDay;
|
|
// Hapus kode yang mungkin mengganggu keyboard
|
|
_fetchEvents();
|
|
_fetchScheduleCount();
|
|
_fetchFieldCount();
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
// Hapus kode yang mungkin mengganggu keyboard
|
|
}
|
|
|
|
Future<void> _fetchEvents() async {
|
|
if (mounted) {
|
|
setState(() => _isLoading = true);
|
|
}
|
|
|
|
try {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Perbaiki query dengan menyebutkan relasi yang spesifik
|
|
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: true);
|
|
|
|
final schedules = response as List<dynamic>;
|
|
|
|
// Convert to event map format
|
|
final eventMap = <DateTime, List<dynamic>>{};
|
|
|
|
for (final schedule in schedules) {
|
|
final startDate = DateTime.parse(schedule['start_date']);
|
|
final endDate = DateTime.parse(schedule['end_date']);
|
|
|
|
// Generate a list of all dates between start and end
|
|
var currentDate = startDate;
|
|
while (currentDate.isBefore(endDate) ||
|
|
currentDate.isAtSameMomentAs(endDate)) {
|
|
final day = DateTime(
|
|
currentDate.year,
|
|
currentDate.month,
|
|
currentDate.day,
|
|
);
|
|
|
|
if (eventMap[day] == null) {
|
|
eventMap[day] = [];
|
|
}
|
|
|
|
eventMap[day]!.add(schedule);
|
|
currentDate = currentDate.add(const Duration(days: 1));
|
|
}
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_events = eventMap;
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(SnackBar(content: Text('Error: ${e.toString()}')));
|
|
}
|
|
}
|
|
}
|
|
|
|
List<dynamic> _getEventsForDay(DateTime day) {
|
|
return _events[DateTime(day.year, day.month, day.day)] ?? [];
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
// Hapus unfocus yang menyebabkan masalah keyboard
|
|
// onTap: () => FocusScope.of(context).unfocus(),
|
|
child: Scaffold(
|
|
resizeToAvoidBottomInset: true,
|
|
appBar: AppBar(
|
|
backgroundColor: const Color(0xFF056839),
|
|
elevation: 0,
|
|
title: Text(
|
|
'Kalender Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
automaticallyImplyLeading: false,
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh),
|
|
tooltip: 'Refresh Data',
|
|
onPressed: () {
|
|
setState(() => _isLoading = true);
|
|
_fetchEvents();
|
|
_fetchScheduleCount();
|
|
_fetchFieldCount();
|
|
},
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.list),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const ScheduleListScreen()),
|
|
).then((_) {
|
|
_fetchEvents();
|
|
_fetchScheduleCount();
|
|
_fetchFieldCount();
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
_buildMonthNavigation(),
|
|
Expanded(
|
|
child: Stack(
|
|
children: [
|
|
_buildCalendar(),
|
|
Positioned(
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
child: _buildStatsRow(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
_buildViewAllButton(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMonthNavigation() {
|
|
return Container(
|
|
color: const Color(0xFF056839),
|
|
padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const SizedBox(width: 40),
|
|
Row(
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.chevron_left,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
onPressed: () {
|
|
setState(() {
|
|
_focusedDay = DateTime(
|
|
_focusedDay.year,
|
|
_focusedDay.month - 1,
|
|
);
|
|
});
|
|
},
|
|
),
|
|
Text(
|
|
DateFormat('MMMM yyyy').format(_focusedDay),
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.chevron_right,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
onPressed: () {
|
|
setState(() {
|
|
_focusedDay = DateTime(
|
|
_focusedDay.year,
|
|
_focusedDay.month + 1,
|
|
);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildCalendar() {
|
|
return Container(
|
|
color: Colors.white,
|
|
child: TableCalendar(
|
|
firstDay: DateTime.utc(2020, 1, 1),
|
|
lastDay: DateTime.utc(2030, 12, 31),
|
|
focusedDay: _focusedDay,
|
|
calendarFormat: _calendarFormat,
|
|
eventLoader: _getEventsForDay,
|
|
startingDayOfWeek: StartingDayOfWeek.monday,
|
|
selectedDayPredicate: (day) {
|
|
return isSameDay(_selectedDay, day);
|
|
},
|
|
onDaySelected: (selectedDay, focusedDay) {
|
|
setState(() {
|
|
_selectedDay = selectedDay;
|
|
_focusedDay = focusedDay;
|
|
});
|
|
_showAddScheduleDialog(selectedDay);
|
|
},
|
|
onFormatChanged: (format) {
|
|
if (_calendarFormat != format) {
|
|
setState(() {
|
|
_calendarFormat = format;
|
|
});
|
|
}
|
|
},
|
|
onPageChanged: (focusedDay) {
|
|
setState(() {
|
|
_focusedDay = focusedDay;
|
|
});
|
|
},
|
|
calendarStyle: CalendarStyle(
|
|
markersMaxCount: 1,
|
|
markerSize: 5,
|
|
markerDecoration: const BoxDecoration(
|
|
color: Color(0xFF056839),
|
|
shape: BoxShape.circle,
|
|
),
|
|
markersAlignment: Alignment.bottomCenter,
|
|
markersAnchor: 0.85,
|
|
canMarkersOverflow: false,
|
|
isTodayHighlighted: true,
|
|
selectedDecoration: const BoxDecoration(
|
|
color: Color(0xFF056839),
|
|
shape: BoxShape.circle,
|
|
),
|
|
todayDecoration: const BoxDecoration(
|
|
color: Color(0xFF056839),
|
|
shape: BoxShape.circle,
|
|
),
|
|
weekendTextStyle: const TextStyle(
|
|
color: Colors.red,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
defaultTextStyle: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black87,
|
|
),
|
|
outsideTextStyle: TextStyle(
|
|
color: Colors.grey[400],
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
selectedTextStyle: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
todayTextStyle: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
cellMargin: EdgeInsets.zero,
|
|
cellPadding: EdgeInsets.zero,
|
|
tableBorder: TableBorder.all(width: 0, color: Colors.transparent),
|
|
),
|
|
headerVisible: false,
|
|
daysOfWeekStyle: DaysOfWeekStyle(
|
|
weekdayStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black87,
|
|
),
|
|
weekendStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.red,
|
|
),
|
|
decoration: const BoxDecoration(color: Colors.white),
|
|
),
|
|
daysOfWeekHeight: 32,
|
|
rowHeight: 42,
|
|
sixWeekMonthsEnforced: false,
|
|
shouldFillViewport: false,
|
|
availableCalendarFormats: const {CalendarFormat.month: 'Bulan'},
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatsRow() {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: _buildStatCard(
|
|
icon: Icons.calendar_today,
|
|
iconColor: const Color(0xFF4CAF50),
|
|
backgroundColor: const Color(0xFFE8F5E8),
|
|
value: '$_activeSchedules',
|
|
label: 'Jadwal',
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: _buildStatCard(
|
|
icon: Icons.grid_view,
|
|
iconColor: const Color(0xFF4CAF50),
|
|
backgroundColor: const Color(0xFFE8F5E8),
|
|
value: '$_totalFields',
|
|
label: 'Lahan',
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: _buildStatCard(
|
|
icon: Icons.trending_up,
|
|
iconColor: const Color(0xFF2196F3),
|
|
backgroundColor: const Color(0xFFE3F2FD),
|
|
value: '0',
|
|
label: 'Progress',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatCard({
|
|
required IconData icon,
|
|
required Color iconColor,
|
|
required Color backgroundColor,
|
|
required String value,
|
|
required String label,
|
|
VoidCallback? onTap,
|
|
}) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
height: 125,
|
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 2),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 3,
|
|
offset: const Offset(0, 1),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
width: 24,
|
|
height: 24,
|
|
decoration: BoxDecoration(
|
|
color: backgroundColor,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(icon, color: iconColor, size: 14),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
value,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
Text(
|
|
label,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 9,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey[600],
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildViewAllButton() {
|
|
return Container(
|
|
margin: const EdgeInsets.all(10),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 42,
|
|
child: ElevatedButton.icon(
|
|
onPressed: () {
|
|
Navigator.pushNamed(context, '/schedule-list');
|
|
},
|
|
icon: const Icon(Icons.list_alt, size: 16, color: Colors.white),
|
|
label: Text(
|
|
'Lihat Semua Jadwal Tanam',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF056839),
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 42,
|
|
child: OutlinedButton.icon(
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const FieldManagementScreen()),
|
|
).then((_) {
|
|
_fetchFieldCount();
|
|
});
|
|
},
|
|
icon: const Icon(Icons.grid_view, size: 16),
|
|
label: Text(
|
|
'Kelola Lahan',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: const Color(0xFF056839),
|
|
side: const BorderSide(color: Color(0xFF056839), width: 1.5),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _showAddScheduleDialog(DateTime selectedDate) async {
|
|
// Dismiss keyboard before showing dialog
|
|
// Hapus unfocus yang mungkin mengganggu keyboard
|
|
|
|
// Show loading indicator first
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => const Center(child: CircularProgressIndicator()),
|
|
);
|
|
|
|
// Fetch existing schedules to avoid conflicts
|
|
List<Map<String, dynamic>> existingSchedules = [];
|
|
try {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user != null) {
|
|
final response = await Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.select('id, field_id, plot, start_date, end_date')
|
|
.eq('user_id', user.id)
|
|
.timeout(const Duration(seconds: 10));
|
|
|
|
existingSchedules = List<Map<String, dynamic>>.from(response);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error fetching existing schedules: $e');
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Gagal memuat jadwal yang ada: ${e.toString()}'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!mounted) return;
|
|
Navigator.of(context).pop(); // Close loading indicator
|
|
|
|
final result = await showDialog(
|
|
context: context,
|
|
barrierDismissible: true,
|
|
builder: (BuildContext context) {
|
|
return AddScheduleDialog(
|
|
existingSchedules: existingSchedules,
|
|
initialStartDate: selectedDate,
|
|
onScheduleAdded: (newSchedule) {
|
|
AppEventBus().fireScheduleUpdated();
|
|
},
|
|
);
|
|
},
|
|
);
|
|
|
|
// Selalu refresh data setelah dialog ditutup
|
|
_fetchEvents();
|
|
_fetchScheduleCount();
|
|
_fetchFieldCount();
|
|
}
|
|
|
|
Future<void> _fetchScheduleCount() async {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
debugPrint('ERROR: User is null in _fetchScheduleCount');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
debugPrint('INFO: Fetching active schedules count');
|
|
|
|
// Add timeout handling to prevent freezing
|
|
final completer = Completer<List<dynamic>>();
|
|
|
|
// Set a timeout to prevent the app from hanging
|
|
Future.delayed(const Duration(seconds: 8), () {
|
|
if (!completer.isCompleted) {
|
|
completer.completeError(
|
|
TimeoutException('Koneksi timeout saat memuat jumlah jadwal.'),
|
|
);
|
|
}
|
|
});
|
|
|
|
// Hapus filter waktu yang mungkin terlalu membatasi
|
|
Supabase.instance.client
|
|
.from('crop_schedules')
|
|
.select()
|
|
.eq('user_id', user.id)
|
|
.eq('status', 'active')
|
|
.then((value) {
|
|
if (!completer.isCompleted) completer.complete(value);
|
|
})
|
|
.catchError((error) {
|
|
if (!completer.isCompleted) completer.completeError(error);
|
|
});
|
|
|
|
final response = await completer.future;
|
|
|
|
debugPrint('INFO: Schedule count response type: ${response.runtimeType}');
|
|
debugPrint('INFO: Raw schedule count response: $response');
|
|
|
|
int count = 0;
|
|
count = response.length;
|
|
debugPrint('INFO: Parsed active schedules count: $count');
|
|
|
|
// Log first schedule for debugging if available
|
|
if (response.isNotEmpty) {
|
|
debugPrint('INFO: First active schedule: ${response.first}');
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_activeSchedules = count;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
debugPrint('ERROR: Error fetching active schedules: $e');
|
|
// Continue silently for count errors - they're not critical
|
|
}
|
|
}
|
|
|
|
Future<void> _fetchFieldCount() async {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
debugPrint('ERROR: User is null in _fetchFieldCount');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
debugPrint('INFO: Fetching fields count');
|
|
|
|
// Add timeout handling to prevent freezing
|
|
final completer = Completer<List<dynamic>>();
|
|
|
|
// Set a timeout to prevent the app from hanging
|
|
Future.delayed(const Duration(seconds: 8), () {
|
|
if (!completer.isCompleted) {
|
|
completer.completeError(
|
|
TimeoutException('Koneksi timeout saat memuat jumlah lahan.'),
|
|
);
|
|
}
|
|
});
|
|
|
|
// Fetch all fields for this user
|
|
Supabase.instance.client
|
|
.from('fields')
|
|
.select()
|
|
.eq('user_id', user.id)
|
|
.then((value) {
|
|
if (!completer.isCompleted) completer.complete(value);
|
|
})
|
|
.catchError((error) {
|
|
if (!completer.isCompleted) completer.completeError(error);
|
|
});
|
|
|
|
final response = await completer.future;
|
|
|
|
debugPrint('INFO: Fields count response type: ${response.runtimeType}');
|
|
debugPrint('INFO: Raw fields count response: $response');
|
|
|
|
int count = 0;
|
|
count = response.length;
|
|
debugPrint('INFO: Parsed fields count: $count');
|
|
|
|
// Log first field for debugging if available
|
|
if (response.isNotEmpty) {
|
|
debugPrint('INFO: First field: ${response.first}');
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_totalFields = count;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
debugPrint('ERROR: Error fetching fields count: $e');
|
|
// Continue silently for count errors - they're not critical
|
|
}
|
|
}
|
|
|
|
Future<void> _fetchSchedules() async {
|
|
if (mounted) {
|
|
setState(() => _isLoading = true);
|
|
}
|
|
|
|
await _fetchEvents();
|
|
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
// Add this method to show exit confirmation dialog
|
|
Future<void> _showExitConfirmationDialog() async {
|
|
return showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: const Text('Konfirmasi'),
|
|
content: const Text('Apakah Anda yakin ingin keluar dari aplikasi?'),
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: const Text('Batal'),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
TextButton(
|
|
child: const Text('Keluar'),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
SystemNavigator.pop();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|