E32221324_Iot_Running/lib/screens/super_user_dashboard.dart

341 lines
16 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'auth/login_screen.dart'; // Pastikan path ini benar
import 'create_event_page.dart'; // Pastikan path ini benar
import 'event_detail_page.dart'; // Pastikan path ini benar
import 'hasil_event_page.dart'; // Pastikan path ini benar
class SuperUserDashboard extends StatefulWidget {
@override
State<SuperUserDashboard> createState() => _SuperUserDashboardState();
}
class _SuperUserDashboardState extends State<SuperUserDashboard> {
// Warna utama yang terinspirasi dari gambar Anda
final Color primaryBlue = Color(0xFF42A5F5); // Biru muda yang cerah
final Color lightBlue = Color(0xFFBBDEFB); // Biru yang lebih terang untuk background
final Color darkBlue = Color(0xFF1976D2); // Biru yang lebih gelap untuk teks/ikon
Future<void> _refresh() async {
// Memaksa rebuild StreamBuilder saat refresh
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white, // Background lembut
body: RefreshIndicator( // RefreshIndicator di sini untuk keseluruhan body
onRefresh: _refresh,
color: primaryBlue, // Warna indicator refresh
child: CustomScrollView(
// Pastikan physics di CustomScrollView agar bisa di-scroll
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
SliverAppBar(
expandedHeight: 200.0, // Tinggi appBar saat expanded
floating: false,
pinned: true, // AppBar tetap terlihat saat scroll
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20), // Sudut melengkung di kiri bawah
bottomRight: Radius.circular(20), // Sudut melengkung di kanan bawah
),
),
backgroundColor: primaryBlue, // Set warna dasar AppBar (penting untuk transisi)
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding: EdgeInsets.only(bottom: 16.0, left: 16.0, right: 16.0),
// Untuk membuat "Dashboard Event" dan logo terlihat melengkung,
// kita bisa membungkus konten background FlexibleSpaceBar
// dengan ClipRRect dan Container tambahan.
background: ClipRRect( // <<< Tambahkan ClipRRect di sini
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
child: Container( // <<< Tambahkan Container di sini
decoration: BoxDecoration(
gradient: LinearGradient( // Tetap gunakan gradient untuk warna
colors: [primaryBlue, darkBlue],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
// Hapus borderRadius di sini karena sudah ada di ClipRRect
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 20), // Spasi untuk logo
Image.asset(
'images/run.png', // Pastikan path logo benar
height: 80,
),
// Jika Anda ingin teks 'Dashboard Event' juga terlihat di expanded state,
// dan melengkung bersama background, Anda bisa menambahkannya di sini.
// Namun, secara default, title FlexibleSpaceBar akan bergerak.
// Untuk membuat teks title 'tetap' dengan background yang melengkung,
// Anda mungkin perlu menempatkan teks di dalam Column ini juga,
// dan menghapus properti 'title' dari FlexibleSpaceBar.
// Untuk saat ini, kita biarkan 'title' seperti adanya untuk animasi.
// Jika Anda ingin title statis di dalam background melengkung, beritahu saya.
],
),
),
),
),
title: Text( // Ini adalah title yang akan bergerak saat scroll
'Dashboard Event',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
actions: [
IconButton(
icon: Icon(Icons.logout, color: Colors.white),
tooltip: 'Logout',
onPressed: () async {
final shouldLogout = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Konfirmasi Logout'),
content: Text('Apakah Anda yakin ingin keluar dari akun Super User?'),
actions: [
TextButton(onPressed: () => Navigator.pop(context, false), child: Text('Batal')),
TextButton(
onPressed: () async {
await FirebaseAuth.instance.signOut();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginScreen()),
(route) => false,
);
},
child: Text('Logout', style: TextStyle(color: Colors.red))),
],
),
);
},
),
],
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Teks "Aksi Cepat" sudah dihapus dari sini sesuai permintaan sebelumnya
_buildActionButton(
context,
icon: Icons.add_circle_outline,
label: 'Buat Event Baru',
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) => CreateEventPage()));
},
color: primaryBlue,
),
SizedBox(height: 12),
_buildActionButton(
context,
icon: Icons.bar_chart,
label: 'Lihat Hasil Event',
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) => HasilEventPage()));
},
color: Colors.green.shade600, // Warna hijau yang bagus
),
],
),
),
),
SizedBox(height: 24),
Text(
'Daftar Event',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: darkBlue,
),
),
SizedBox(height: 1),
],
),
),
),
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('events')
.orderBy('created_at', descending: true) // Urutkan terbaru di atas
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return SliverFillRemaining(
child: Center(child: CircularProgressIndicator(color: primaryBlue)));
}
if (snapshot.hasError) {
return SliverFillRemaining(
child: Center(child: Text('Error: ${snapshot.error}', style: TextStyle(color: Colors.red))));
}
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return SliverFillRemaining(child: Center(child: Text('Belum ada event yang dibuat.')));
}
final events = snapshot.data!.docs;
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final data = events[index].data() as Map<String, dynamic>;
final docId = events[index].id;
final eventName = data['name'] ?? 'Tanpa Nama Event';
final totalLaps = data['total_laps'] ?? 0;
final totalDistance = data['total_distance'] ?? 0;
final createdAt = data['created_at'] is Timestamp
? (data['created_at'] as Timestamp).toDate()
: null;
final isStartedPermanently = data['is_started_permanently'] ?? false;
final isFinishedPermanently = data['is_finished_permanently'] ?? false;
String statusText;
Color statusColor;
if (isFinishedPermanently) {
statusText = 'Selesai';
statusColor = Colors.green.shade700;
} else if (isStartedPermanently) {
statusText = 'Sedang Berlangsung';
statusColor = Colors.orange.shade700;
} else {
statusText = 'Belum Dimulai';
statusColor = darkBlue;
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Card(
elevation: 5,
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EventDetailPage(
eventId: docId,
eventName: eventName,
),
),
);
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
eventName,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: darkBlue,
),
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.flag_outlined, size: 18, color: Colors.grey[700]),
SizedBox(width: 8),
Text(
'$totalLaps Putaran (${(totalDistance / 1000).toStringAsFixed(2)} KM)', // Format KM dengan 2 desimal
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
),
],
),
SizedBox(height: 4),
Row(
children: [
Icon(Icons.event, size: 18, color: Colors.grey[700]),
SizedBox(width: 8),
Text(
createdAt != null ? '${createdAt.day}/${createdAt.month}/${createdAt.year}' : 'Tanggal tidak diketahui',
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
),
],
),
SizedBox(height: 8),
Align(
alignment: Alignment.bottomRight,
child: Chip(
label: Text(
statusText,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
backgroundColor: statusColor,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
shape: StadiumBorder(),
),
),
],
),
),
),
),
);
},
childCount: events.length,
),
);
},
),
],
),
),
);
}
// Helper method untuk membuat tombol aksi yang konsisten
Widget _buildActionButton(
BuildContext context, {
required IconData icon,
required String label,
required VoidCallback onPressed,
required Color color,
}) {
return SizedBox(
width: double.infinity, // Membuat tombol full width
child: ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, color: Colors.white),
label: Text(
label,
style: TextStyle(fontSize: 16, color: Colors.white, fontWeight: FontWeight.bold),
),
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 5,
),
),
);
}
}