842 lines
28 KiB
Dart
842 lines
28 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:intl/intl.dart';
|
||
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'package:http/http.dart' as http;
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
import '../../../services/auth_service.dart';
|
||
import '../../../services/notification_service.dart';
|
||
import '../../../screens/dashboard/user/notification_screen.dart';
|
||
|
||
class UserHome extends StatefulWidget {
|
||
const UserHome({super.key});
|
||
|
||
@override
|
||
State<UserHome> createState() => _UserHomeState();
|
||
}
|
||
|
||
class _UserHomeState extends State<UserHome> {
|
||
late String _timeString;
|
||
late Timer _timer;
|
||
late Timer _notifTimer;
|
||
|
||
String? fotoUser;
|
||
String namaUser = '-';
|
||
String shift = '-';
|
||
String jamShift = '-';
|
||
String lokasiShift = '-';
|
||
|
||
String laporanJudul = '-';
|
||
String laporanWaktu = '-';
|
||
String laporanDeskripsi = '-';
|
||
|
||
int _unreadCount = 0;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
|
||
_timeString = DateFormat('HH:mm:ss').format(DateTime.now());
|
||
|
||
_timer = Timer.periodic(
|
||
const Duration(seconds: 1),
|
||
(Timer t) => _updateTime(),
|
||
);
|
||
|
||
loadData();
|
||
|
||
_notifTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
|
||
_loadUnreadCount();
|
||
});
|
||
}
|
||
|
||
Future<void> loadData() async {
|
||
await getUser();
|
||
await fetchJadwal();
|
||
await fetchLaporanTerakhir();
|
||
|
||
await NotificationService.checkJadwalNotification();
|
||
await NotificationService.checkLaporanDitolakNotification();
|
||
await _loadUnreadCount();
|
||
}
|
||
|
||
Future<void> _loadUnreadCount() async {
|
||
final list = await NotificationService.getLocalNotifications();
|
||
if (mounted) {
|
||
setState(() {
|
||
_unreadCount = list.where((n) => !n.isRead).length;
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_timer.cancel();
|
||
_notifTimer.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
void _updateTime() {
|
||
final formattedTime = DateFormat('HH:mm:ss').format(DateTime.now());
|
||
if (mounted) {
|
||
setState(() {
|
||
_timeString = formattedTime;
|
||
});
|
||
}
|
||
}
|
||
|
||
String getTanggalHari() {
|
||
final now = DateTime.now();
|
||
return DateFormat('EEEE, dd MMMM yyyy', 'id_ID').format(now);
|
||
}
|
||
|
||
Future<void> getUser() async {
|
||
final prefs = await SharedPreferences.getInstance();
|
||
|
||
namaUser = prefs.getString('nama_user') ?? '-';
|
||
fotoUser = prefs.getString('foto');
|
||
|
||
if (fotoUser == null || fotoUser == '') {
|
||
final result = await AuthService.getProfile();
|
||
if (result['success']) {
|
||
fotoUser = result['data']['foto'];
|
||
}
|
||
}
|
||
|
||
if (mounted) setState(() {});
|
||
}
|
||
|
||
Future<void> fetchJadwal() async {
|
||
String? token = await AuthService.getToken();
|
||
|
||
try {
|
||
|
||
final response = await http.get(
|
||
Uri.parse("${AuthService.baseUrl}/jadwal"),
|
||
headers: {
|
||
"Accept": "application/json",
|
||
"Authorization": "Bearer $token",
|
||
},
|
||
);
|
||
|
||
if (response.statusCode == 200) {
|
||
|
||
final data = jsonDecode(response.body);
|
||
|
||
debugPrint("JADWAL RESPONSE: ${response.body}");
|
||
|
||
if (data['jadwal'] != null &&
|
||
data['jadwal'].length > 0) {
|
||
|
||
final now = DateTime.now();
|
||
|
||
final today = DateFormat(
|
||
'yyyy-MM-dd',
|
||
).format(now);
|
||
|
||
// semua shift hari ini
|
||
final jadwalHariIni = data['jadwal']
|
||
.where((j) =>
|
||
j['tanggal'] == today &&
|
||
j['status_jadwal'] != 'libur' &&
|
||
j['nama_shift'] != null)
|
||
.toList();
|
||
|
||
if (jadwalHariIni.isEmpty) return;
|
||
|
||
dynamic shiftAktif;
|
||
|
||
for (var jadwal in jadwalHariIni) {
|
||
|
||
try {
|
||
|
||
final mulai = jadwal['jam_mulai'];
|
||
final selesai = jadwal['jam_selesai'];
|
||
|
||
final mulaiParts = mulai.split(':');
|
||
final selesaiParts = selesai.split(':');
|
||
|
||
final mulaiDate = DateTime(
|
||
now.year,
|
||
now.month,
|
||
now.day,
|
||
int.parse(mulaiParts[0]),
|
||
int.parse(mulaiParts[1]),
|
||
);
|
||
|
||
final selesaiDate = DateTime(
|
||
now.year,
|
||
now.month,
|
||
now.day,
|
||
int.parse(selesaiParts[0]),
|
||
int.parse(selesaiParts[1]),
|
||
);
|
||
|
||
// kalau sekarang di antara shift
|
||
if (!now.isBefore(mulaiDate) &&
|
||
now.isBefore(selesaiDate)) {
|
||
|
||
shiftAktif = jadwal;
|
||
break;
|
||
|
||
}
|
||
|
||
} catch (e) {
|
||
debugPrint("ERROR PARSE SHIFT: $e");
|
||
}
|
||
}
|
||
|
||
// kalau tidak ada shift aktif,
|
||
// ambil shift terakhir hari ini
|
||
shiftAktif ??= jadwalHariIni.last;
|
||
|
||
debugPrint("SHIFT AKTIF: $shiftAktif");
|
||
|
||
setState(() {
|
||
|
||
shift =
|
||
shiftAktif['nama_shift'] ?? '-';
|
||
|
||
jamShift =
|
||
"${shiftAktif['jam_mulai']} - ${shiftAktif['jam_selesai']}";
|
||
|
||
lokasiShift =
|
||
shiftAktif['nama_lokasi'] ?? '-';
|
||
|
||
});
|
||
}
|
||
}
|
||
|
||
} catch (e) {
|
||
|
||
debugPrint("Error fetch jadwal: $e");
|
||
|
||
}
|
||
}
|
||
|
||
Future<void> fetchLaporanTerakhir() async {
|
||
String? token = await AuthService.getToken();
|
||
try {
|
||
final response = await http.get(
|
||
Uri.parse("${AuthService.baseUrl}/laporan"),
|
||
headers: {
|
||
"Accept": "application/json",
|
||
"Authorization": "Bearer $token",
|
||
},
|
||
);
|
||
|
||
if (response.statusCode == 200) {
|
||
final data = jsonDecode(response.body);
|
||
debugPrint("LAPORAN: ${response.body}");
|
||
if (data['data'] != null && data['data'].length > 0) {
|
||
final laporan = data['data'][0];
|
||
setState(() {
|
||
laporanJudul = laporan['jadwal']?['lokasi']?['nama_lokasi']
|
||
?? laporan['lokasi']?['nama_lokasi']
|
||
?? laporan['nama_lokasi']
|
||
?? '-';
|
||
laporanDeskripsi = laporan['keterangan'] ?? '-';
|
||
laporanWaktu = laporan['waktu_patroli']
|
||
?? laporan['created_at']
|
||
?? '-';
|
||
});
|
||
}
|
||
}
|
||
} catch (e) {
|
||
debugPrint("Error fetch laporan: $e");
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: const Color(0xFFF8F9FD),
|
||
body: SingleChildScrollView(
|
||
child: Column(
|
||
children: [
|
||
// ================= HEADER =================
|
||
Stack(
|
||
clipBehavior: Clip.none,
|
||
children: [
|
||
Container(
|
||
height: 240,
|
||
width: double.infinity,
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [Color(0xFF1A39B1), Color(0xFF4263EB)],
|
||
begin: Alignment.topLeft,
|
||
end: Alignment.bottomRight,
|
||
),
|
||
borderRadius: BorderRadius.only(
|
||
bottomLeft: Radius.circular(40),
|
||
bottomRight: Radius.circular(40),
|
||
),
|
||
),
|
||
padding: const EdgeInsets.fromLTRB(25, 60, 25, 20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Flexible(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: const [
|
||
Text(
|
||
'Patroli Kampus',
|
||
style: TextStyle(
|
||
color: Colors.white,
|
||
fontSize: 22,
|
||
fontWeight: FontWeight.bold,
|
||
letterSpacing: 0.8,
|
||
),
|
||
),
|
||
Text(
|
||
'Politeknik Negeri Jember',
|
||
style: TextStyle(
|
||
color: Colors.white70,
|
||
fontSize: 12,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
Row(
|
||
children: [
|
||
Stack(
|
||
children: [
|
||
IconButton(
|
||
icon: const Icon(
|
||
Icons.notifications_rounded,
|
||
color: Colors.white,
|
||
size: 28,
|
||
),
|
||
onPressed: () async {
|
||
await Navigator.push(
|
||
context,
|
||
MaterialPageRoute(
|
||
builder: (_) =>
|
||
const NotificationPage(),
|
||
),
|
||
);
|
||
await _loadUnreadCount();
|
||
},
|
||
),
|
||
if (_unreadCount > 0)
|
||
Positioned(
|
||
right: 6,
|
||
top: 6,
|
||
child: Container(
|
||
padding: const EdgeInsets.all(5),
|
||
decoration: const BoxDecoration(
|
||
color: Colors.red,
|
||
shape: BoxShape.circle,
|
||
),
|
||
constraints: const BoxConstraints(
|
||
minWidth: 18,
|
||
minHeight: 18,
|
||
),
|
||
child: Text(
|
||
_unreadCount > 9
|
||
? '9+'
|
||
: '$_unreadCount',
|
||
style: const TextStyle(
|
||
color: Colors.white,
|
||
fontSize: 10,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
|
||
const SizedBox(width: 8),
|
||
|
||
Hero(
|
||
tag: 'logo_polije',
|
||
child: Image.asset(
|
||
'assets/images/logopolije.png',
|
||
height: 45,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
|
||
const SizedBox(height: 20),
|
||
|
||
// DATE & TIME
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
vertical: 8,
|
||
horizontal: 12,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withValues(alpha: 0.15),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
const Icon(
|
||
Icons.calendar_today_rounded,
|
||
size: 14,
|
||
color: Colors.white,
|
||
),
|
||
const SizedBox(width: 8),
|
||
Text(
|
||
getTanggalHari(),
|
||
style: const TextStyle(
|
||
color: Colors.white,
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
const SizedBox(width: 15),
|
||
const VerticalDivider(
|
||
color: Colors.white54,
|
||
thickness: 1,
|
||
),
|
||
const Icon(
|
||
Icons.watch_later_rounded,
|
||
size: 14,
|
||
color: Colors.white,
|
||
),
|
||
const SizedBox(width: 8),
|
||
Text(
|
||
_timeString,
|
||
style: const TextStyle(
|
||
color: Colors.white,
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
// ================= PROFILE CARD =================
|
||
Padding(
|
||
padding: const EdgeInsets.only(top: 185, left: 20, right: 20),
|
||
child: Container(
|
||
padding: const EdgeInsets.all(18),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(24),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.black.withValues(alpha: 0.06),
|
||
blurRadius: 20,
|
||
offset: const Offset(0, 10),
|
||
),
|
||
],
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
padding: const EdgeInsets.all(3),
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
border: Border.all(
|
||
color: const Color(0xFF4263EB),
|
||
width: 2,
|
||
),
|
||
),
|
||
child: CircleAvatar(
|
||
radius: 26,
|
||
backgroundColor: const Color(0xFFF0F3FF),
|
||
backgroundImage:
|
||
(fotoUser != null && fotoUser != '')
|
||
? NetworkImage(fotoUser!)
|
||
: null,
|
||
child: (fotoUser == null || fotoUser == '')
|
||
? const Icon(
|
||
Icons.person_rounded,
|
||
color: Color(0xFF1A39B1),
|
||
size: 30,
|
||
)
|
||
: null,
|
||
),
|
||
),
|
||
|
||
const SizedBox(width: 15),
|
||
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'Selamat Bertugas,',
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
color: Colors.grey,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
Text(
|
||
namaUser,
|
||
style: const TextStyle(
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.bold,
|
||
color: Color(0xFF1E293B),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
const Icon(
|
||
Icons.verified_user_rounded,
|
||
color: Colors.green,
|
||
size: 20,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
|
||
// ================= CONTENT =================
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(20, 40, 20, 20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
_buildSectionTitle("Shift Hari Ini"),
|
||
const SizedBox(height: 15),
|
||
_buildShiftCard(
|
||
shift: shift,
|
||
jam: jamShift,
|
||
lokasi: lokasiShift,
|
||
),
|
||
const SizedBox(height: 30),
|
||
_buildSectionTitle("Laporan Terakhir"),
|
||
const SizedBox(height: 15),
|
||
_buildLaporanCard(
|
||
judul: laporanJudul,
|
||
waktu: laporanWaktu,
|
||
deskripsi: laporanDeskripsi,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSectionTitle(String title) {
|
||
return Row(
|
||
children: [
|
||
Container(
|
||
width: 4,
|
||
height: 18,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF3B82F6),
|
||
borderRadius: BorderRadius.circular(10),
|
||
),
|
||
),
|
||
const SizedBox(width: 10),
|
||
Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontWeight: FontWeight.w800,
|
||
fontSize: 18,
|
||
color: Color(0xFF1E293B),
|
||
letterSpacing: 0.2,
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget _buildShiftCard({
|
||
required String shift,
|
||
required String jam,
|
||
required String lokasi,
|
||
}) {
|
||
// Hitung durasi
|
||
String durasi = '-';
|
||
try {
|
||
final parts = jam.split(' - ');
|
||
if (parts.length == 2) {
|
||
final mulai = parts[0].trim().split(':');
|
||
final selesai = parts[1].trim().split(':');
|
||
final jamMulai = int.parse(mulai[0]);
|
||
final jamSelesai = int.parse(selesai[0]);
|
||
int diff = jamSelesai - jamMulai;
|
||
if (diff < 0) diff += 24;
|
||
durasi = '$diff jam';
|
||
}
|
||
} catch (_) {}
|
||
|
||
return IntrinsicHeight(
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
// Left accent bar
|
||
Container(
|
||
width: 4,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF2F5BEA),
|
||
borderRadius: const BorderRadius.only(
|
||
topLeft: Radius.circular(16),
|
||
bottomLeft: Radius.circular(16),
|
||
),
|
||
),
|
||
),
|
||
// Card body
|
||
Expanded(
|
||
child: Container(
|
||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 14),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
border: Border(
|
||
top: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
right: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
bottom: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
),
|
||
borderRadius: const BorderRadius.only(
|
||
topRight: Radius.circular(16),
|
||
bottomRight: Radius.circular(16),
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// Nama shift + badge
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
shift == '-' ? 'Tidak ada shift' : shift,
|
||
style: TextStyle(
|
||
fontSize: 13,
|
||
color: Colors.grey.shade600,
|
||
),
|
||
),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFE8F0FE),
|
||
borderRadius: BorderRadius.circular(20),
|
||
),
|
||
child: const Text(
|
||
'Aktif',
|
||
style: TextStyle(
|
||
fontSize: 10,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF2F5BEA),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 6),
|
||
// Jam besar
|
||
Text(
|
||
jam == '-' ? '--:-- – --:--' : jam,
|
||
style: const TextStyle(
|
||
fontSize: 28,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1E293B),
|
||
letterSpacing: -0.5,
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
'Durasi $durasi',
|
||
style: TextStyle(fontSize: 11, color: Colors.grey.shade400),
|
||
),
|
||
const SizedBox(height: 14),
|
||
// Divider
|
||
Divider(height: 1, color: Colors.grey.shade100),
|
||
const SizedBox(height: 12),
|
||
// 2 col bawah
|
||
Row(
|
||
children: [
|
||
// Durasi
|
||
Expanded(
|
||
child: Row(
|
||
children: [
|
||
const Icon(Icons.access_time_rounded, size: 13, color: Color(0xFF2F5BEA)),
|
||
const SizedBox(width: 7),
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('Durasi', style: TextStyle(fontSize: 11, color: Colors.grey.shade400)),
|
||
Text(durasi, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF1E293B))),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
// Divider vertikal
|
||
Container(width: 0.5, height: 30, color: Colors.grey.shade200),
|
||
const SizedBox(width: 14),
|
||
// Lokasi
|
||
Expanded(
|
||
child: Row(
|
||
children: [
|
||
const Icon(Icons.location_on_rounded, size: 13, color: Color(0xFF2F5BEA)),
|
||
const SizedBox(width: 7),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('Lokasi', style: TextStyle(fontSize: 11, color: Colors.grey.shade400)),
|
||
Text(
|
||
lokasi == '-' ? '-' : lokasi,
|
||
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF1E293B)),
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildLaporanCard({
|
||
required String judul,
|
||
required String waktu,
|
||
required String deskripsi,
|
||
}) {
|
||
// Format waktu
|
||
String waktuFormatted = waktu;
|
||
try {
|
||
final dt = DateTime.parse(waktu);
|
||
waktuFormatted = DateFormat('dd MMM yyyy · HH:mm', 'id_ID').format(dt);
|
||
} catch (_) {}
|
||
|
||
return IntrinsicHeight(
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
Container(
|
||
width: 4,
|
||
decoration: const BoxDecoration(
|
||
color: Color(0xFF2F5BEA),
|
||
borderRadius: BorderRadius.only(
|
||
topLeft: Radius.circular(16),
|
||
bottomLeft: Radius.circular(16),
|
||
),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
border: Border(
|
||
top: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
right: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
bottom: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||
),
|
||
borderRadius: const BorderRadius.only(
|
||
topRight: Radius.circular(16),
|
||
bottomRight: Radius.circular(16),
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// Header
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 12),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
const Icon(
|
||
Icons.location_on_rounded,
|
||
size: 13,
|
||
color: Color(0xFF2F5BEA),
|
||
),
|
||
const SizedBox(width: 5),
|
||
Text(
|
||
judul == '-' ? 'Belum ada laporan' : judul,
|
||
style: const TextStyle(
|
||
fontSize: 13,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1E293B),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
Text(
|
||
waktuFormatted,
|
||
style: TextStyle(
|
||
fontSize: 11,
|
||
color: Colors.grey.shade400,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Divider(height: 1, color: Colors.grey.shade100),
|
||
// Preview teks besar
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||
child: Text(
|
||
deskripsi == '-' ? 'Tidak ada keterangan.' : deskripsi,
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1E293B),
|
||
height: 1.4,
|
||
),
|
||
),
|
||
),
|
||
Divider(height: 1, color: Colors.grey.shade100),
|
||
// Footer
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 10, 16, 12),
|
||
child: Row(
|
||
children: [
|
||
const Icon(
|
||
Icons.access_time_rounded,
|
||
size: 11,
|
||
color: Color(0xFF2F5BEA),
|
||
),
|
||
const SizedBox(width: 5),
|
||
Text(
|
||
'Dilaporkan $waktuFormatted',
|
||
style: TextStyle(
|
||
fontSize: 11,
|
||
color: Colors.grey.shade400,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|