From cdb1a4c860b4df7c29b85a417f97b1f44b79be3d Mon Sep 17 00:00:00 2001 From: Adam Darmawansa Date: Fri, 4 Jul 2025 19:05:25 +0700 Subject: [PATCH] Fixed app Kode program sukses aplikasi --- app.drawio | 261 ++ newata2/.env | 6 +- newata2/README.md | 106 + newata2/db_scheme.sql | 31 + newata2/images/cctv.png | Bin 0 -> 245382 bytes newata2/lib/auth_wrapper.dart | 31 + newata2/lib/constant.dart | 4 - newata2/lib/dashboard_page.dart | 473 ++++ newata2/lib/login_page.dart | 175 ++ newata2/lib/main copy 4.dart | 2155 +++++++++++++++++ newata2/lib/main copy 5.dart | 1051 ++++++++ newata2/lib/main.dart | 226 +- newata2/lib/provider.dart | 206 ++ newata2/lib/providers/auth_provider.dart | 106 - .../providers/pantau_footage_provider.dart | 71 - newata2/lib/providers/schedule_provider.dart | 120 - newata2/lib/register_page.dart | 124 + newata2/lib/screens/control_screen.dart | 722 ------ newata2/lib/screens/dashboard_screen.dart | 82 - newata2/lib/screens/footage_screen.dart | 404 --- newata2/lib/screens/image_viewer_screen.dart | 60 - newata2/lib/screens/login_screen.dart | 207 -- newata2/lib/screens/register_screen.dart | 239 -- newata2/lib/screens/splash_screen.dart | 57 - newata2/lib/screens/video_player_screen.dart | 152 -- newata2/lib/splash_screen.dart | 54 + .../Flutter/GeneratedPluginRegistrant.swift | 6 - newata2/notify-all-user.ts | 76 + newata2/pubspec.lock | 136 -- newata2/pubspec.yaml | 6 +- 30 files changed, 4799 insertions(+), 2548 deletions(-) create mode 100644 app.drawio create mode 100644 newata2/db_scheme.sql create mode 100644 newata2/images/cctv.png create mode 100644 newata2/lib/auth_wrapper.dart delete mode 100644 newata2/lib/constant.dart create mode 100644 newata2/lib/dashboard_page.dart create mode 100644 newata2/lib/login_page.dart create mode 100644 newata2/lib/main copy 4.dart create mode 100644 newata2/lib/main copy 5.dart create mode 100644 newata2/lib/provider.dart delete mode 100644 newata2/lib/providers/auth_provider.dart delete mode 100644 newata2/lib/providers/pantau_footage_provider.dart delete mode 100644 newata2/lib/providers/schedule_provider.dart create mode 100644 newata2/lib/register_page.dart delete mode 100644 newata2/lib/screens/control_screen.dart delete mode 100644 newata2/lib/screens/dashboard_screen.dart delete mode 100644 newata2/lib/screens/footage_screen.dart delete mode 100644 newata2/lib/screens/image_viewer_screen.dart delete mode 100644 newata2/lib/screens/login_screen.dart delete mode 100644 newata2/lib/screens/register_screen.dart delete mode 100644 newata2/lib/screens/splash_screen.dart delete mode 100644 newata2/lib/screens/video_player_screen.dart create mode 100644 newata2/lib/splash_screen.dart create mode 100644 newata2/notify-all-user.ts diff --git a/app.drawio b/app.drawio new file mode 100644 index 0000000..1c2bffb --- /dev/null +++ b/app.drawio @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/newata2/.env b/newata2/.env index 194dd47..ba2cfe3 100644 --- a/newata2/.env +++ b/newata2/.env @@ -1,3 +1,3 @@ -SUPABASE_URL=https://gtpvcrpnojbjzoedqemy.supabase.co -SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd0cHZjcnBub2pianpvZWRxZW15Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDEwMDY2NjMsImV4cCI6MjA1NjU4MjY2M30.PDDMcfnpVtZS7FTYi-qXejTS10uGKPmwzrcOXhutUsk -SUPABASE_sECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd0cHZjcnBub2pianpvZWRxZW15Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDEwMDY2NjMsImV4cCI6MjA1NjU4MjY2M30.PDDMcfnpVtZS7FTYi-qXejTS10uGKPmwzrcOXhutUsk \ No newline at end of file +SUPABASE_URL=https://ihuetazxejsioxcjejpj.supabase.co +SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlodWV0YXp4ZWpzaW94Y2planBqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTExMDEyMTgsImV4cCI6MjA2NjY3NzIxOH0.IKFyrKioiScfAS_9UouPEcesHHoas0SDZH0mZBCA1ro + diff --git a/newata2/README.md b/newata2/README.md index d6d1ea8..10a927c 100644 --- a/newata2/README.md +++ b/newata2/README.md @@ -14,3 +14,109 @@ A few resources to get you started if this is your first Flutter project: For help getting started with Flutter development, view the [online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. + +---------------------------------------------------------------------------------------------------------------------------------------------------------- + +Penjelasan Detail per Bagian Kode +1. main.dart +Ini adalah titik masuk (entry point) dari aplikasi Anda. + +Future main() async: Fungsi main dibuat async karena kita perlu menunggu proses inisialisasi selesai sebelum aplikasi berjalan. + +WidgetsFlutterBinding.ensureInitialized(): Baris ini wajib ada ketika Anda ingin menjalankan kode sebelum runApp(), memastikan semua binding Flutter siap. + +await dotenv.load(...): Memuat semua variabel dari file .env Anda (seperti URL dan Kunci Supabase) ke dalam memori. + +await Supabase.initialize(...): Menginisialisasi koneksi ke proyek Supabase Anda. Ini adalah langkah krusial. + +ChangeNotifierProvider(...): Ini berasal dari library provider. Ini "membungkus" seluruh aplikasi Anda dengan AppState, sehingga semua halaman di bawahnya dapat "mendengarkan" dan bereaksi terhadap perubahan data di AppState. + +MyApp: Widget utama yang mengatur tema global aplikasi dan mendefinisikan halaman awal. + +2. AppState (lib/provider/app_state.dart) +Ini adalah "otak" dari aplikasi Anda. Kelas ini mengelola semua data dan logika bisnis aplikasi (state management). + +class AppState extends ChangeNotifier: ChangeNotifier adalah kelas dari Flutter yang memungkinkan AppState untuk "memberi tahu" para pendengarnya (widget) ketika ada data yang berubah, menggunakan notifyListeners(). + +Variabel State (_): Variabel yang diawali dengan _ (contoh: _isLoading) bersifat private. Data ini hanya bisa diubah dari dalam kelas AppState itu sendiri. Widget lain hanya bisa membacanya melalui getters (contoh: isLoading). + +Constructor AppState(): Di sini kita mengatur pendengar onAuthStateChange. Ini adalah pendengar realtime dari Supabase yang aktif setiap kali ada perubahan status login (login, logout). Jika pengguna logout (_currentUser == null), kita memanggil clearState() untuk membersihkan semua data sesi sebelumnya. + +_setLoading(bool value): Fungsi internal untuk mengontrol tampilan CircularProgressIndicator di seluruh aplikasi. + +setDeviceIdAndFetchData(...): Fungsi ini dipanggil setelah login berhasil. Ia menyimpan deviceId, kemudian memanggil fungsi lain untuk mengambil data awal dan mulai mendengarkan perubahan realtime. + +fetchEvents() & fetchDeviceStatus(): Fungsi ini mengambil data dari tabel Supabase (sensor_events dan device_status) menggunakan select(). Data ini kemudian disimpan dalam variabel state. + +_listenToRealtimeChanges() (PERBAIKAN ERROR): + +Fungsi ini sekarang menggunakan sintaks baru dari supabase_flutter v2. + +supabase.channel(...): Membuat channel komunikasi realtime. + +.onPostgresChanges(...): Ini adalah fungsi spesifik untuk mendengarkan perubahan pada database PostgreSQL Anda. + +event: PostgresChangeEvent.insert: Hanya mendengarkan event INSERT (ketika ada data baru masuk). + +schema: 'public', table: '...': Menentukan tabel mana yang ingin dipantau. + +filter: ...: Filter tambahan agar kita hanya mendapatkan notifikasi untuk deviceId yang sedang dipantau. + +.listen((payload) { ... }): Callback yang akan dieksekusi ketika ada data baru. payload.new berisi data baris yang baru saja ditambahkan. + +setSleepSchedule(...): Fungsi ini dipanggil saat pengguna menekan tombol "Set Timer". Ia melakukan update() pada tabel device_status, mengisi kolom schedule_duration_microseconds dan setter_user_id dengan nilai yang baru. + +3. Halaman SplashScreen (Baru) +StatefulWidget: Diperlukan karena kita perlu mengelola state selama proses navigasi. + +initState(): Metode ini dipanggil sekali saat widget pertama kali dibuat. Di sinilah kita memulai timer. + +Future.delayed(const Duration(seconds: 3), ...): Menjalankan kode di dalamnya setelah jeda 3 detik. + +Navigator.pushReplacement(...): Setelah 3 detik, kita pindah ke AuthWrapper. pushReplacement digunakan agar pengguna tidak bisa kembali ke splash screen dengan menekan tombol "back". + +4. AuthWrapper +Seperti yang dijelaskan, ini adalah widget tanpa tampilan yang berfungsi sebagai router cerdas. + +Consumer: Widget ini secara otomatis "mendengarkan" AppState. Setiap kali notifyListeners() dipanggil di AppState, bagian builder dari Consumer ini akan dieksekusi ulang. + +if (appState.currentUser != null && appState.deviceId != null): Logika inti. Jika ada pengguna yang login DAN deviceId sudah diatur, tampilkan DashboardPage. Jika tidak, tampilkan LoginPage. + +5. LoginPage +StatefulWidget & TextEditingController: Digunakan untuk mengelola input teks dari pengguna. + +_formKey: Digunakan untuk validasi form (memastikan kolom tidak kosong). + +_signIn(): + +Memeriksa validasi form. + +Memanggil supabase.auth.signInWithPassword(...). + +Jika berhasil, ia memanggil Provider.of(context, listen: false).setDeviceIdAndFetchData(...) untuk memberi tahu AppState bahwa pengguna telah login dengan deviceId tertentu, sehingga AppState bisa mulai mengambil data. + +Menangani AuthException jika login gagal (misalnya, password salah). + +6. RegisterPage +Mirip dengan LoginPage, tetapi memanggil supabase.auth.signUp(...). + +data: {'full_name': ...}: Ini adalah bagian penting. Di sinilah kita menyimpan data tambahan (nama lengkap) ke dalam kolom raw_user_meta_data di tabel auth.users Supabase. + +7. DashboardPage & Komponen-komponennya +DashboardPage: Menggunakan CustomScrollView dan Sliver untuk layout yang efisien dan memungkinkan adanya refresh indicator. + +ProfileBar: Mengambil data pengguna (fullName, email) dari AppState dan menampilkannya. Tombol logout memanggil appState.signOut(). + +SettingsBar: + +Menampilkan status online/offline berdasarkan data dari _deviceStatus di AppState. + +Tombol "Set Timer" hanya aktif jika isOnline == true. + +_showSetTimerDialog(): Memunculkan TimePicker bawaan Flutter. Logikanya menghitung selisih antara waktu sekarang dan waktu yang dipilih pengguna, mengonversinya ke mikrodetik, lalu memanggil appState.setSleepSchedule() untuk mengirim data ke Supabase. + +FootageGallery & FootageCard: + +FootageGallery menampilkan SliverList dari FootageCard berdasarkan data _events di AppState. + +FootageCard adalah kartu individu yang menampilkan gambar (Image.network), lokasi, tipe event, dan waktu kejadian. Desain kartu dibuat dengan BoxDecoration untuk memberikan efek bayangan (mirip 3D) dan sudut yang tumpul (borderRadius). \ No newline at end of file diff --git a/newata2/db_scheme.sql b/newata2/db_scheme.sql new file mode 100644 index 0000000..9ae47c0 --- /dev/null +++ b/newata2/db_scheme.sql @@ -0,0 +1,31 @@ +-- 1. TABEL UNTUK MENCATAT SEMUA EVENT DARI SENSOR +CREATE TABLE public.sensor_events ( + id TEXT PRIMARY KEY NOT NULL, -- Format: device_id + epoch. Contoh: garasi_01_1672531200 + created_at TIMESTAMPTZ DEFAULT now() NOT NULL, + device_id TEXT NOT NULL, + event_timestamp TIMESTAMPTZ NOT NULL, -- Waktu asli saat event terjadi (dari RTC) + event_type TEXT NOT NULL, -- Contoh: 'GERAKAN' atau 'GETARAN' + location_name TEXT, + image_ref TEXT -- URL publik ke gambar di Supabase Storage +); +COMMENT ON TABLE public.sensor_events IS 'Mencatat setiap event yang terdeteksi oleh sensor.'; + +-- 2. TABEL UNTUK MEMANTAU STATUS PERANGKAT DAN JADWAL SLEEP +CREATE TABLE public.device_status ( + device_id TEXT PRIMARY KEY NOT NULL, -- Contoh: 'garasi_01' + updated_at TIMESTAMPTZ DEFAULT now() NOT NULL, + is_online BOOLEAN DEFAULT false, + last_status_update TIMESTAMPTZ, + schedule_duration_microseconds BIGINT DEFAULT 0, -- Durasi sleep dalam mikrodetik + setter_user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL -- Foreign Key ke tabel pengguna +); +COMMENT ON TABLE public.device_status IS 'Memantau status online/offline dan jadwal deep sleep perangkat.'; + +-- Tambahkan trigger untuk otomatis memperbarui kolom 'updated_at' +create extension if not exists moddatetime schema extensions; + +create trigger handle_updated_at before update on public.device_status + for each row execute procedure extensions.moddatetime (updated_at); + +create trigger handle_updated_at_events before update on public.sensor_events + for each row execute procedure extensions.moddatetime (created_at); diff --git a/newata2/images/cctv.png b/newata2/images/cctv.png new file mode 100644 index 0000000000000000000000000000000000000000..3597b19dc4fc29484d1d8ba4bad40222d09d0cd6 GIT binary patch literal 245382 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

7E8$bE3pT^Z!e;rekbCfR)9Okcf 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

_y>@9F8UctMH7hbP|y8SHl6HZIIVl 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 -y@Xy?X}xkgLo_L6Xb0 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

KN??GtmI*Asmo?y3-PJCa9z#_|ipn6!X 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�x%rq` zWEt1Tw`#?4QT@3*PKrzim@n?nkWL5x z8NZ%mEPd{~*7jIwHfbw78*jq;)Lg?C{3r(U*}vHl&hjf3HTjrk8xOW0mRESG=JB1^@msEZz!^JcUxZ~anVE6~OiT!O5j z4iFu1eYAtFiL2cuxEBmUW>%frm;#xSU4T6qPp=E#!;@O0)1=jH>aWbG zn;IEx&`J@3+e--)nYv}-#-N5=sb^Ld2KxK`f+YF@_~)GqxqnG@P5Bt0N}1r*x}b3P zm(6-9by%*Npn$B~>bNDqszmNJ@MKc?@_mVJ$+k}kZocZ8c;OXS$Md#si+$=#`j_Wp zOMho<8|>CZv@1p$oiRM9xjy5#25tu1hRVXDos^@ur`MmMToi6WmKSav( + stream: supabase.auth.onAuthStateChange, + builder: (context, snapshot) { + // Tampilkan loading spinner selagi menunggu event auth pertama. + if (!snapshot.hasData) { + return const Scaffold( + body: Center(child: CircularProgressIndicator()), + ); + } + + // Sesuai alur aplikasi, selalu mulai dari LoginPage untuk meminta Device ID, + // terlepas dari status sesi sebelumnya. + return const LoginPage(); + }, + ); + } +} diff --git a/newata2/lib/constant.dart b/newata2/lib/constant.dart deleted file mode 100644 index aa7e866..0000000 --- a/newata2/lib/constant.dart +++ /dev/null @@ -1,4 +0,0 @@ -class AppConstants { - static const String footageBucket = - 'newata-footage-bucket'; // Ganti dengan nama bucket Anda -} diff --git a/newata2/lib/dashboard_page.dart b/newata2/lib/dashboard_page.dart new file mode 100644 index 0000000..6d121b7 --- /dev/null +++ b/newata2/lib/dashboard_page.dart @@ -0,0 +1,473 @@ +// ================================================================= +// DASHBOARD PAGE & COMPONENTS +// ================================================================= + +import 'package:CCTV_App/login_page.dart'; +import 'package:CCTV_App/provider.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +// ================================================================= +// DASHBOARD PAGE & COMPONENTS +// ================================================================= + +class DashboardPage extends StatelessWidget { + const DashboardPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + Consumer( + builder: (context, appState, child) { + if (appState.isLoading && appState.events.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return RefreshIndicator( + onRefresh: Provider.of(context, listen: false) + .fetchInitialData, + color: Colors.tealAccent, + backgroundColor: const Color(0xFF2c2c2c), + child: const CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: ProfileBar()), + SliverToBoxAdapter(child: SettingsBar()), + SliverToBoxAdapter(child: SizedBox(height: 20)), + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text("Footage Gallery", + style: TextStyle( + fontSize: 22, fontWeight: FontWeight.bold)), + )), + SliverToBoxAdapter(child: SizedBox(height: 10)), + FootageGallery(), + ], + ), + ); + }, + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: Consumer( + builder: (context, appState, child) { + return Visibility( + visible: appState.isLoading, + child: const LinearProgressIndicator( + color: Colors.tealAccent, + backgroundColor: Colors.transparent), + ); + }, + ), + ) + ], + ), + ), + ); + } +} + +class ProfileBar extends StatelessWidget { + const ProfileBar({super.key}); + + @override + Widget build(BuildContext context) { + final user = Provider.of(context, listen: false).currentUser; + final fullName = user?.userMetadata?['full_name'] ?? 'No Name'; + final email = user?.email ?? 'No Email'; + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + CircleAvatar( + backgroundColor: Colors.teal, + child: Text(fullName.isNotEmpty ? fullName[0].toUpperCase() : 'U', + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold)), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(fullName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white)), + Text(email, + style: + const TextStyle(fontSize: 14, color: Colors.white70)), + ], + ), + ), + IconButton( + tooltip: "Logout", + icon: const Icon(Icons.logout, color: Colors.white70), + onPressed: () async { + final bool? confirmLogout = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: const Color(0xFF2c2c2c), + title: const Text('Konfirmasi Logout', + style: TextStyle(color: Colors.white)), + content: const Text('Apakah Anda yakin ingin keluar?', + style: TextStyle(color: Colors.white70)), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Batal', + style: TextStyle(color: Colors.white70)), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Logout', + style: TextStyle(color: Colors.redAccent)), + ), + ], + ); + }, + ); + + if (!context.mounted) return; + + if (confirmLogout == true) { + // PERBAIKAN: Navigasi dulu, baru proses logout di latar belakang. + final appState = Provider.of(context, listen: false); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => + const LoginPage(showLogoutSuccess: true)), + (route) => false, + ); + + await appState.signOut(); + } + }, + ) + ], + ), + ); + } +} + +class SettingsBar extends StatelessWidget { + const SettingsBar({super.key}); + + void _showSetTimerDialog(BuildContext context, AppState appState) async { + final now = DateTime.now(); + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(now.add(const Duration(hours: 1))), + ); + + if (pickedTime != null) { + DateTime scheduledTime = DateTime( + now.year, now.month, now.day, pickedTime.hour, pickedTime.minute); + if (scheduledTime.isBefore(now)) { + scheduledTime = scheduledTime.add(const Duration(days: 1)); + } + + final duration = scheduledTime.difference(now); + final durationMicroseconds = duration.inMicroseconds; + + final error = await appState.setSleepSchedule(durationMicroseconds); + + if (context.mounted) { + if (error == null) { + final formattedTime = DateFormat.jm('id_ID').format(scheduledTime); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Jadwal deep sleep diatur hingga $formattedTime'), + backgroundColor: Colors.green, + )); + } else { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Gagal mengatur jadwal: $error'), + backgroundColor: Colors.redAccent, + )); + } + } + } + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + final statusData = appState.deviceStatus; + + if (statusData == null) { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Center( + child: Text("Mencari status perangkat...", + style: TextStyle(color: Colors.white24))), + ); + } + + final deviceId = statusData['device_id'] ?? 'N/A'; + final deviceStatus = statusData['status'] ?? 'unknown'; + final isOnline = deviceStatus == 'active'; + final lastUpdateString = + statusData['last_update'] ?? DateTime.now().toIso8601String(); + final lastUpdate = DateTime.parse(lastUpdateString).toLocal(); + final formattedLastUpdate = + DateFormat('dd MMM, HH:mm:ss', 'id_ID').format(lastUpdate); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Device: $deviceId', + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + Row( + children: [ + Icon(Icons.circle, + color: isOnline + ? Colors.greenAccent + : Colors.redAccent, + size: 12), + const SizedBox(width: 8), + Text(isOnline ? 'Active' : 'Inactive', + style: const TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 8), + Text( + 'Last update: $formattedLastUpdate', + style: const TextStyle( + fontSize: 12, color: Colors.white54), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + const SizedBox(width: 12), + ElevatedButton( + onPressed: isOnline + ? () => _showSetTimerDialog(context, appState) + : null, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + foregroundColor: Colors.white, + disabledBackgroundColor: Colors.grey.withOpacity(0.3)), + child: const Text('Set Timer'), + ) + ], + ), + ), + ); + }, + ); + } +} + +class FootageGallery extends StatelessWidget { + const FootageGallery({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + final events = appState.events; + if (events.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(48.0), + child: Column( + children: [ + Icon(CupertinoIcons.photo_on_rectangle, + size: 60, color: Colors.white24), + SizedBox(height: 16), + Text('Belum ada tangkapan', + style: TextStyle(color: Colors.white24, fontSize: 16)), + ], + ), + ), + ), + ); + } + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final event = events[index]; + return FootageCard(event: event); + }, + childCount: events.length, + ), + ); + }, + ); + } +} + +class FootageCard extends StatelessWidget { + final Map event; + const FootageCard({super.key, required this.event}); + + @override + Widget build(BuildContext context) { + final imageUrl = event['image_ref'] as String?; + final location = event['location'] ?? 'Unknown Location'; + final eventType = event['event_type'] ?? 'Unknown Event'; + final timestampString = + event['timestamp'] ?? DateTime.now().toIso8601String(); + final timestamp = DateTime.parse(timestampString); + + final formattedTime = DateFormat('EEEE, dd MMMM yyyy, HH:mm:ss', 'id_ID') + .format(timestamp.toLocal()); + + String displayEventType = 'Unknown'; + Color eventColor = Colors.grey; + if (eventType == 'motion') { + displayEventType = 'Motion Detected'; + eventColor = Colors.orange.shade800; + } else if (eventType == 'vibration') { + displayEventType = 'Vibration Detected'; + eventColor = Colors.purple.shade800; + } + + return Container( + margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 4), + ) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (imageUrl != null && imageUrl.isNotEmpty) + Image.network( + imageUrl, + fit: BoxFit.cover, + width: double.infinity, + height: 250, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + height: 250, + color: Colors.black12, + child: Center( + child: CircularProgressIndicator( + color: Colors.tealAccent, + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) => Container( + height: 250, + color: Colors.black12, + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.exclamationmark_triangle, + color: Colors.redAccent, size: 50), + SizedBox(height: 8), + Text("Gagal memuat gambar", + style: TextStyle(color: Colors.white70)) + ], + ), + ), + ), + ) + else + Container( + height: 100, + color: Colors.black12, + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.videocam, + size: 30, color: Colors.white24), + SizedBox(height: 8), + Text("No image captured", + style: TextStyle(color: Colors.white24)) + ], + )), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: eventColor, + borderRadius: BorderRadius.circular(8)), + child: Text( + displayEventType, + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ), + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(CupertinoIcons.location_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(location, + style: const TextStyle( + fontSize: 16, color: Colors.white)), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon(CupertinoIcons.time_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(formattedTime, + style: const TextStyle( + fontSize: 14, color: Colors.white70)), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/newata2/lib/login_page.dart b/newata2/lib/login_page.dart new file mode 100644 index 0000000..99dfa33 --- /dev/null +++ b/newata2/lib/login_page.dart @@ -0,0 +1,175 @@ +// import 'package:CCTV_App/dashboard_page.dart'; +import 'package:CCTV_App/dashboard_page.dart'; +import 'package:CCTV_App/main.dart'; +import 'package:CCTV_App/provider.dart'; +import 'package:CCTV_App/register_page.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +class LoginPage extends StatefulWidget { + final bool showLogoutSuccess; + + const LoginPage({super.key, this.showLogoutSuccess = false}); + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _deviceIdController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + // PERBAIKAN: Menambahkan initState untuk menampilkan snackbar jika diperlukan. + @override + void initState() { + super.initState(); + if (widget.showLogoutSuccess) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Anda telah berhasil logout.'), + backgroundColor: Colors.green, + behavior: SnackBarBehavior.floating, + ), + ); + } + }); + } + } + + Future _signIn() async { + if (!_formKey.currentState!.validate()) return; + + setState(() => _isLoading = true); + final appState = Provider.of(context, listen: false); + + try { + final authResponse = await supabase.auth.signInWithPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + + if (authResponse.user == null) { + throw 'Login gagal, pengguna tidak ditemukan.'; + } + + final deviceIdIsValid = await appState + .setDeviceIdAndFetchData(_deviceIdController.text.trim()); + + if (!mounted) return; + + if (deviceIdIsValid) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const DashboardPage()), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Login berhasil, tetapi Device ID tidak terdaftar.'), + backgroundColor: Colors.orange, + )); + await appState.signOut(); + } + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Terjadi kesalahan: ${e.toString()}'), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + final userEmail = supabase.auth.currentUser?.email; + if (userEmail != null && _emailController.text.isEmpty) { + _emailController.text = userEmail; + } + + return Scaffold( + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.shield_lefthalf_fill, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Security Monitor', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.isEmpty ? 'Password tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _deviceIdController, + decoration: const InputDecoration(labelText: 'Device ID'), + validator: (value) => + value!.isEmpty ? 'Device ID tidak boleh kosong' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signIn, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.black)) + : const Text('Login', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + const SizedBox(height: 24), + TextButton( + onPressed: () => Navigator.push(context, + MaterialPageRoute(builder: (_) => const RegisterPage())), + child: const Text('Belum punya akun? Daftar sekarang', + style: TextStyle(color: Colors.white70)), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/newata2/lib/main copy 4.dart b/newata2/lib/main copy 4.dart new file mode 100644 index 0000000..319c7d9 --- /dev/null +++ b/newata2/lib/main copy 4.dart @@ -0,0 +1,2155 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:intl/intl.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:provider/provider.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; +import 'dart:async'; + +// ================================================================= +// MAIN.DART & SETUP +// ================================================================= + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(fileName: ".env"); + + await Supabase.initialize( + url: dotenv.env['SUPABASE_URL']!, + anonKey: dotenv.env['SUPABASE_ANON_KEY']!, + ); + + await initializeDateFormatting('id_ID', null); + + runApp( + ChangeNotifierProvider( + create: (context) => AppState(), + child: const MyApp(), + ), + ); +} + +final supabase = Supabase.instance.client; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Security Monitor', + theme: ThemeData( + brightness: Brightness.dark, + primaryColor: Colors.tealAccent, + scaffoldBackgroundColor: const Color(0xFF1a1a1a), + cardColor: const Color(0xFF2c2c2c), + colorScheme: const ColorScheme.dark( + primary: Colors.tealAccent, + secondary: Colors.teal, + surface: Color(0xFF2c2c2c), + onSurface: Colors.white, + ), + textTheme: const TextTheme( + bodyLarge: TextStyle(color: Colors.white70), + bodyMedium: TextStyle(color: Colors.white70), + titleLarge: + TextStyle(color: Colors.white, fontWeight: FontWeight.bold), + titleMedium: TextStyle(color: Colors.white), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: Colors.black.withOpacity(0.3), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none), + labelStyle: const TextStyle(color: Colors.white54)), + useMaterial3: true, + ), + home: const SplashScreen(), + debugShowCheckedModeBanner: false, + ); + } +} + +// ================================================================= +// STATE MANAGEMENT (PROVIDER) +// ================================================================= + +class AppState extends ChangeNotifier { + // State variables + bool _isLoading = false; + String? _deviceId; + List> _events = []; + // PENYESUAIAN: Nama variabel diubah agar lebih jelas + Map? _scheduleStatus; + User? _currentUser; + + // Realtime channel subscriptions + RealtimeChannel? _sensorEventsChannel; + RealtimeChannel? _scheduleStatusChannel; // PENYESUAIAN: Nama channel diubah + + // Getters + bool get isLoading => _isLoading; + String? get deviceId => _deviceId; + List> get events => _events; + // PENYESUAIAN: Nama getter diubah + Map? get scheduleStatus => _scheduleStatus; + User? get currentUser => _currentUser; + + // Constructor + AppState() { + _currentUser = supabase.auth.currentUser; + supabase.auth.onAuthStateChange.listen((data) { + _currentUser = data.session?.user; + if (_currentUser == null) { + clearState(); + } + notifyListeners(); + }); + } + + void _unsubscribeFromChannels() { + if (_sensorEventsChannel != null) { + supabase.removeChannel(_sensorEventsChannel!); + _sensorEventsChannel = null; + } + // PENYESUAIAN: Channel yang di-unsubscribe disesuaikan + if (_scheduleStatusChannel != null) { + supabase.removeChannel(_scheduleStatusChannel!); + _scheduleStatusChannel = null; + } + } + + void clearState() { + _deviceId = null; + _events.clear(); + _scheduleStatus = null; + _unsubscribeFromChannels(); + notifyListeners(); + } + + void _setLoading(bool value) { + _isLoading = value; + notifyListeners(); + } + + // PENYESUAIAN: Fungsi ini sekarang menangani validasi device ID + // dan mengembalikan boolean yang menandakan keberhasilan. + Future setDeviceIdAndFetchData(String deviceId) async { + // FITUR BARU: Validasi Device ID + try { + final response = await supabase + .from('devices') + .select('id') + .eq('id', deviceId) + .maybeSingle(); // maybeSingle() mengembalikan null jika tidak ditemukan + + if (response == null) { + return false; // Device ID tidak valid + } + + // Jika valid, lanjutkan seperti biasa + _deviceId = deviceId; + await fetchInitialData(); + _listenToRealtimeChanges(); + notifyListeners(); + return true; // Sukses + } catch (e) { + print('Error validating device ID: $e'); + return false; + } + } + + Future fetchInitialData() async { + if (_deviceId == null) return; + _setLoading(true); + await Future.wait([ + fetchEvents(), + fetchScheduleStatus(), // PENYESUAIAN: Nama fungsi diubah + ]); + _setLoading(false); + } + + Future fetchEvents() async { + if (_deviceId == null) return; + try { + final response = await supabase + .from('sensor_events') + .select() + .eq('device_id', _deviceId!) + .order('created_at', ascending: false); // Kolom diubah ke created_at + _events = List>.from(response); + } catch (e) { + print('Error fetching events: $e'); + _events = []; + } + notifyListeners(); + } + + // PENYESUAIAN: Nama fungsi diubah dan logika disederhanakan + Future fetchScheduleStatus() async { + try { + // Mengambil dari tabel 'device_schedules' dengan id=1 + final response = await supabase + .from('device_schedules') + .select() + .eq('id', 1) + .single(); + _scheduleStatus = response; + } catch (e) { + print('Error fetching schedule status: $e'); + _scheduleStatus = null; + } + notifyListeners(); + } + + // DIHAPUS: Fungsi fetchSetterUserDetails tidak lagi diperlukan + // karena nama sudah ada di tabel device_schedules. + + void _listenToRealtimeChanges() { + if (_deviceId == null) return; + _unsubscribeFromChannels(); + + // Channel untuk sensor_events tetap sama + _sensorEventsChannel = supabase.channel('public:sensor_events'); + _sensorEventsChannel! + .onPostgresChanges( + event: PostgresChangeEvent.insert, + schema: 'public', + table: 'sensor_events', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'device_id', + value: _deviceId!, + ), + callback: (payload) { + final newEvent = payload.newRecord; + if (!_events.any((e) => e['id'] == newEvent['id'])) { + _events.insert(0, newEvent); + notifyListeners(); + } + }, + ) + .subscribe(); + + // PENYESUAIAN: Channel untuk mendengarkan perubahan jadwal + _scheduleStatusChannel = supabase.channel('public:device_schedules'); + _scheduleStatusChannel! + .onPostgresChanges( + event: PostgresChangeEvent.update, // Cukup dengarkan UPDATE + schema: 'public', + table: 'device_schedules', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'id', // Filter berdasarkan ID baris + value: 1, // Hanya baris dengan id=1 + ), + callback: (payload) { + // Logika disederhanakan, langsung update status + _scheduleStatus = payload.newRecord; + notifyListeners(); + }, + ) + .subscribe(); + } + + Future signOut() async { + _setLoading(true); + await supabase.auth.signOut(); + // clearState() akan dipanggil otomatis oleh listener onAuthStateChange + _setLoading(false); + } + + // PENYESUAIAN: Fungsi ini sekarang mengirim nama lengkap pengguna + Future setSleepSchedule(int durationMicroseconds) async { + if (_currentUser == null) { + return "User not identified."; + } + _setLoading(true); + + final fullName = _currentUser!.userMetadata?['full_name'] ?? 'Unknown User'; + + try { + // Update tabel 'device_schedules' baris id=1 + await supabase.from('device_schedules').update({ + 'schedule_duration_microseconds': durationMicroseconds, + 'setter_user_id': _currentUser!.id, + 'setter_full_name': fullName, // FITUR BARU: Kirim nama lengkap + 'updated_at': DateTime.now().toIso8601String(), + }).eq('id', 1); + + _setLoading(false); + return null; + } on PostgrestException catch (e) { + _setLoading(false); + print('Error setting sleep schedule: ${e.message}'); + return e.message; + } catch (e) { + _setLoading(false); + print('Generic error setting sleep schedule: $e'); + return 'An unexpected error occurred.'; + } + } +} + +// ================================================================= +// SPLASH SCREEN, AUTH WRAPPER, REGISTER PAGE (Tidak ada perubahan signifikan) +// ================================================================= + +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + @override + void initState() { + super.initState(); + _redirect(); + } + + Future _redirect() async { + await Future.delayed(const Duration(seconds: 2)); + if (mounted) { + final session = supabase.auth.currentSession; + final deviceId = Provider.of(context, listen: false).deviceId; + + // Jika ada sesi DAN deviceId sudah di-set, langsung ke Dashboard + if (session != null && deviceId != null && deviceId.isNotEmpty) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const DashboardPage())); + } else { + // Jika tidak, ke halaman login + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const LoginPage())); + } + } + } + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.lock_shield_fill, + size: 120, color: Colors.tealAccent), + SizedBox(height: 24), + Text('Security Monitor', + style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + SizedBox(height: 80), + CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white54)), + ], + ), + ), + ); + } +} + +class AuthWrapper extends StatelessWidget { + const AuthWrapper({super.key}); + @override + Widget build(BuildContext context) { + // Disederhanakan: Logika redirect kini ditangani di SplashScreen dan LoginPage + // AuthWrapper bisa dihapus atau disederhanakan jika mau, + // tapi kita biarkan untuk potensi routing di masa depan. + final appState = Provider.of(context); + if (appState.currentUser != null && appState.deviceId != null) { + return const DashboardPage(); + } else { + return const LoginPage(); + } + } +} + +class RegisterPage extends StatefulWidget { + const RegisterPage({super.key}); + + @override + State createState() => _RegisterPageState(); +} + +class _RegisterPageState extends State { + final _fullNameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + Future _signUp() async { + if (_formKey.currentState!.validate()) { + setState(() => _isLoading = true); + try { + await supabase.auth.signUp( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + data: {'full_name': _fullNameController.text.trim()}, + ); + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Registrasi berhasil! Silakan periksa email Anda untuk verifikasi dan kemudian login.'), + backgroundColor: Colors.green, + )); + Navigator.pop(context); + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) { + setState(() => _isLoading = false); + } + } + } + } + + @override + void dispose() { + _fullNameController.dispose(); + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + ), + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.person_add_solid, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Buat Akun Baru', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _fullNameController, + decoration: const InputDecoration(labelText: 'Nama Lengkap'), + validator: (value) => + value!.isEmpty ? 'Nama tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.length < 6 ? 'Password minimal 6 karakter' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signUp, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.black, + )) + : const Text('Register', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + ], + ), + ), + ), + ), + ); + } +} + +// ================================================================= +// LOGIN PAGE +// ================================================================= + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _deviceIdController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + // PENYESUAIAN: Logika sign in diubah total untuk validasi Device ID + Future _signIn() async { + if (!_formKey.currentState!.validate()) return; + + setState(() => _isLoading = true); + + try { + // 1. Coba login dengan email dan password + final authResponse = await supabase.auth.signInWithPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + + if (authResponse.user == null) { + // Ini seharusnya tidak terjadi jika tidak ada AuthException, tapi sebagai penjaga + throw 'Login failed unexpectedly.'; + } + + // 2. Jika login berhasil, validasi Device ID + final appState = Provider.of(context, listen: false); + final deviceIdIsValid = await appState.setDeviceIdAndFetchData( + _deviceIdController.text.trim() + ); + + if (!mounted) return; + + if (deviceIdIsValid) { + // 3. Jika Device ID valid, navigasi ke Dashboard + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const DashboardPage()), + ); + } else { + // 4. Jika Device ID TIDAK valid, tampilkan error dan logout lagi + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Login berhasil, tetapi Device ID tidak ditemukan.'), + backgroundColor: Colors.orange, + )); + await supabase.auth.signOut(); // Logout untuk mencegah state aneh + } + + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Terjadi kesalahan: ${e.toString()}'), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) { + setState(() => _isLoading = false); + } + } + } + + @override + void dispose() { + _emailController.dispose(); + _passwordController.dispose(); + _deviceIdController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.shield_lefthalf_fill, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Security Monitor', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.isEmpty ? 'Password tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _deviceIdController, + decoration: const InputDecoration(labelText: 'Device ID'), + validator: (value) => + value!.isEmpty ? 'Device ID tidak boleh kosong' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signIn, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.black, + )) + : const Text('Login', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + const SizedBox(height: 24), + TextButton( + onPressed: () => Navigator.push(context, + MaterialPageRoute(builder: (_) => const RegisterPage())), + child: const Text('Belum punya akun? Daftar sekarang', + style: TextStyle(color: Colors.white70)), + ), + ], + ), + ), + ), + ), + ); + } +} + + +// ================================================================= +// DASHBOARD PAGE & COMPONENTS +// ================================================================= + +class DashboardPage extends StatelessWidget { + const DashboardPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + Consumer( + builder: (context, appState, child) { + if (appState.isLoading && appState.events.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return RefreshIndicator( + onRefresh: Provider.of(context, listen: false) + .fetchInitialData, + color: Colors.tealAccent, + backgroundColor: const Color(0xFF2c2c2c), + child: const CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: ProfileBar()), + SliverToBoxAdapter(child: SettingsBar()), + SliverToBoxAdapter(child: SizedBox(height: 20)), + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text("Footage Gallery", + style: TextStyle( + fontSize: 22, fontWeight: FontWeight.bold)), + )), + SliverToBoxAdapter(child: SizedBox(height: 10)), + FootageGallery(), + ], + ), + ); + }, + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: Consumer( + builder: (context, appState, child) { + return Visibility( + visible: appState.isLoading, + child: const LinearProgressIndicator( + color: Colors.tealAccent, + backgroundColor: Colors.transparent, + ), + ); + }, + ), + ) + ], + ), + ), + ); + } +} + +class ProfileBar extends StatelessWidget { + const ProfileBar({super.key}); + + @override + Widget build(BuildContext context) { + final appState = Provider.of(context); + final user = appState.currentUser; + final fullName = user?.userMetadata?['full_name'] ?? 'No Name'; + final email = user?.email ?? 'No Email'; + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + CircleAvatar( + backgroundColor: Colors.teal, + child: Text(fullName.isNotEmpty ? fullName[0].toUpperCase() : 'U', + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold)), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(fullName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white)), + Text(email, + style: + const TextStyle(fontSize: 14, color: Colors.white70)), + ], + ), + ), + IconButton( + tooltip: "Logout", + icon: const Icon(Icons.logout, color: Colors.white70), + onPressed: () { + Provider.of(context, listen: false).signOut(); + // Navigasi kembali ke LoginPage setelah logout + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (context) => const LoginPage()), + (route) => false, + ); + }, + ) + ], + ), + ); + } +} + + +class SettingsBar extends StatelessWidget { + const SettingsBar({super.key}); + + void _showSetTimerDialog(BuildContext context, AppState appState) async { + final now = DateTime.now(); + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(now.add(const Duration(hours: 1))), + ); + + if (pickedTime != null) { + DateTime scheduledTime = DateTime( + now.year, now.month, now.day, pickedTime.hour, pickedTime.minute); + if (scheduledTime.isBefore(now)) { + scheduledTime = scheduledTime.add(const Duration(days: 1)); + } + + final duration = scheduledTime.difference(now); + // ESP32-CAM menggunakan mikrodetik untuk deep sleep + final durationMicroseconds = duration.inMicroseconds; + + final error = await appState.setSleepSchedule(durationMicroseconds); + + if (context.mounted) { + if (error == null) { + final formattedTime = DateFormat.jm('id_ID').format(scheduledTime); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Jadwal deep sleep diatur hingga $formattedTime'), + backgroundColor: Colors.green, + )); + } else { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Gagal mengatur jadwal: $error'), + backgroundColor: Colors.redAccent, + )); + } + } + } + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + // PENYESUAIAN: Mengambil data dari 'scheduleStatus' + final status = appState.scheduleStatus; + final deviceId = appState.deviceId ?? 'N/A'; + + if (status == null) { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Center( + child: Text("Mencari status perangkat...", + style: TextStyle(color: Colors.white24))), + ); + } + + // Asumsi perangkat online jika aplikasi bisa mengambil data. + // Logika status on/off bisa dibuat lebih kompleks jika diperlukan. + final bool isOnline = true; + + // FITUR BARU: Mengambil nama dari kolom baru 'setter_full_name' + final String setterName = status['setter_full_name'] ?? 'Belum pernah diatur'; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Device: $deviceId', + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + Row( + children: [ + Icon(Icons.circle, + color: isOnline + ? Colors.greenAccent + : Colors.redAccent, + size: 12), + const SizedBox(width: 8), + Text(isOnline ? 'Online' : 'Offline', + style: const TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 8), + // PENYESUAIAN: Menampilkan nama setter + Text( + 'Last Setter: $setterName', + style: const TextStyle( + fontSize: 12, color: Colors.white54), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + const SizedBox(width: 12), + ElevatedButton( + onPressed: isOnline + ? () => _showSetTimerDialog(context, appState) + : null, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + foregroundColor: Colors.white, + disabledBackgroundColor: Colors.grey.withOpacity(0.3)), + child: const Text('Set Timer'), + ) + ], + ), + ), + ); + }, + ); + } +} + +class FootageGallery extends StatelessWidget { + const FootageGallery({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + final events = appState.events; + if (events.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(48.0), + child: Column( + children: [ + Icon(CupertinoIcons.photo_on_rectangle, + size: 60, color: Colors.white24), + SizedBox(height: 16), + Text('Belum ada tangkapan', + style: TextStyle(color: Colors.white24, fontSize: 16)), + ], + ), + ), + ), + ); + } + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final event = events[index]; + return FootageCard(event: event); + }, + childCount: events.length, + ), + ); + }, + ); + } +} + +class FootageCard extends StatelessWidget { + final Map event; + const FootageCard({super.key, required this.event}); + + @override + Widget build(BuildContext context) { + final imageUrl = event['image_ref'] ?? ''; + final location = event['location_name'] ?? 'Unknown Location'; + final eventType = event['event_type'] ?? 'Unknown Event'; + + // PENYESUAIAN: Menggunakan kolom 'created_at' yang pasti ada + final timestampString = event['created_at'] ?? DateTime.now().toIso8601String(); + final timestamp = DateTime.parse(timestampString); + + final formattedTime = DateFormat('EEEE, dd MMMM yyyy, HH:mm:ss', 'id_ID') + .format(timestamp.toLocal()); + + return Container( + margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 4), + ) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (imageUrl.isNotEmpty) + Image.network( + imageUrl, + fit: BoxFit.cover, + width: double.infinity, + height: 250, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + height: 250, + color: Colors.black12, + child: Center( + child: CircularProgressIndicator( + color: Colors.tealAccent, + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) => Container( + height: 250, + color: Colors.black12, + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.exclamationmark_triangle, + color: Colors.redAccent, size: 50), + SizedBox(height: 8), + Text("Gagal memuat gambar", + style: TextStyle(color: Colors.white70)) + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: eventType == 'GERAKAN' + ? Colors.orange.shade800 + : Colors.purple.shade800, + borderRadius: BorderRadius.circular(8)), + child: Text( + eventType, + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ), + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(CupertinoIcons.location_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(location, + style: const TextStyle( + fontSize: 16, color: Colors.white)), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon(CupertinoIcons.time_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(formattedTime, + style: const TextStyle( + fontSize: 14, color: Colors.white70)), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} + + + + + + + + + + + + + + + + + + + + + + + +// import 'package:flutter/material.dart'; +// import 'package:flutter/cupertino.dart'; +// import 'package:flutter_dotenv/flutter_dotenv.dart'; +// import 'package:intl/intl.dart'; +// import 'package:intl/date_symbol_data_local.dart'; // <-- IMPORT PENTING +// import 'package:provider/provider.dart'; +// import 'package:supabase_flutter/supabase_flutter.dart'; +// import 'dart:async'; + +// // ================================================================= +// // MAIN.DART & SETUP +// // ================================================================= + +// Future main() async { +// // Pastikan semua binding Flutter siap sebelum menjalankan kode async +// WidgetsFlutterBinding.ensureInitialized(); + +// // Muat environment variables dari file .env +// await dotenv.load(fileName: ".env"); + +// // Inisialisasi Supabase +// await Supabase.initialize( +// url: dotenv.env['SUPABASE_URL']!, +// anonKey: dotenv.env['SUPABASE_ANON_KEY']!, +// ); + +// // ================================================================= +// // PERBAIKAN UTAMA: Inisialisasi locale untuk package 'intl' +// // Ini akan memuat data format tanggal untuk Bahasa Indonesia ('id_ID'). +// // Panggilan ini sangat penting untuk mencegah LocaleDataException. +// // ================================================================= +// await initializeDateFormatting('id_ID', null); + +// runApp( +// ChangeNotifierProvider( +// create: (context) => AppState(), +// child: const MyApp(), +// ), +// ); +// } + +// // Global Supabase client +// final supabase = Supabase.instance.client; + +// class MyApp extends StatelessWidget { +// const MyApp({super.key}); + +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// title: 'Security Monitor', +// theme: ThemeData( +// brightness: Brightness.dark, +// primaryColor: Colors.tealAccent, +// scaffoldBackgroundColor: const Color(0xFF1a1a1a), +// cardColor: const Color(0xFF2c2c2c), +// colorScheme: const ColorScheme.dark( +// primary: Colors.tealAccent, +// secondary: Colors.teal, +// surface: Color(0xFF2c2c2c), +// onSurface: Colors.white, +// ), +// textTheme: const TextTheme( +// bodyLarge: TextStyle(color: Colors.white70), +// bodyMedium: TextStyle(color: Colors.white70), +// titleLarge: +// TextStyle(color: Colors.white, fontWeight: FontWeight.bold), +// titleMedium: TextStyle(color: Colors.white), +// ), +// inputDecorationTheme: InputDecorationTheme( +// filled: true, +// fillColor: Colors.black.withOpacity(0.3), +// border: OutlineInputBorder( +// borderRadius: BorderRadius.circular(12), +// borderSide: BorderSide.none), +// labelStyle: const TextStyle(color: Colors.white54)), +// useMaterial3: true, +// ), +// // Set SplashScreen sebagai halaman awal +// home: const SplashScreen(), +// debugShowCheckedModeBanner: false, +// ); +// } +// } + +// // ================================================================= +// // STATE MANAGEMENT (PROVIDER) +// // ================================================================= + +// class AppState extends ChangeNotifier { +// // State variables +// bool _isLoading = false; +// String? _deviceId; +// List> _events = []; +// Map? _deviceStatus; +// User? _currentUser; +// Map? _setterUserDetails; + +// // Realtime channel subscriptions +// RealtimeChannel? _sensorEventsChannel; +// RealtimeChannel? _deviceStatusChannel; + +// // Getters +// bool get isLoading => _isLoading; +// String? get deviceId => _deviceId; +// List> get events => _events; +// Map? get deviceStatus => _deviceStatus; +// User? get currentUser => _currentUser; +// Map? get setterUserDetails => _setterUserDetails; + +// // Constructor +// AppState() { +// _currentUser = supabase.auth.currentUser; +// supabase.auth.onAuthStateChange.listen((data) { +// _currentUser = data.session?.user; +// if (_currentUser == null) { +// clearState(); +// } +// notifyListeners(); +// }); +// } + +// void _unsubscribeFromChannels() { +// if (_sensorEventsChannel != null) { +// supabase.removeChannel(_sensorEventsChannel!); +// _sensorEventsChannel = null; +// } +// if (_deviceStatusChannel != null) { +// supabase.removeChannel(_deviceStatusChannel!); +// _deviceStatusChannel = null; +// } +// } + +// void clearState() { +// _deviceId = null; +// _events.clear(); +// _deviceStatus = null; +// _setterUserDetails = null; +// _unsubscribeFromChannels(); // Unsubscribe saat logout +// notifyListeners(); +// } + +// void _setLoading(bool value) { +// _isLoading = value; +// notifyListeners(); +// } + +// Future setDeviceIdAndFetchData(String deviceId) async { +// _deviceId = deviceId; +// if (_deviceId != null && _deviceId!.isNotEmpty) { +// await fetchInitialData(); +// _listenToRealtimeChanges(); +// } +// notifyListeners(); +// } + +// Future fetchInitialData() async { +// if (_deviceId == null) return; +// _setLoading(true); +// await Future.wait([ +// fetchEvents(), +// fetchDeviceStatus(), +// ]); +// _setLoading(false); +// } + +// Future fetchEvents() async { +// if (_deviceId == null) return; +// try { +// final response = await supabase +// .from('sensor_events') +// .select() +// .eq('device_id', _deviceId!) +// .order('event_timestamp', ascending: false); +// _events = List>.from(response); +// } catch (e) { +// print('Error fetching events: $e'); +// _events = []; +// } +// notifyListeners(); +// } + +// Future fetchDeviceStatus() async { +// if (_deviceId == null) return; +// try { +// final response = await supabase +// .from('device_status') +// .select() +// .eq('device_id', _deviceId!) +// .single(); +// _deviceStatus = response; +// if (_deviceStatus != null && _deviceStatus!['setter_user_id'] != null) { +// await fetchSetterUserDetails(_deviceStatus!['setter_user_id']); +// } else { +// _setterUserDetails = null; +// } +// } catch (e) { +// print('Error fetching device status: $e'); +// _deviceStatus = {'device_id': _deviceId, 'is_online': false}; +// } +// notifyListeners(); +// } + +// Future fetchSetterUserDetails(String userId) async { +// try { +// final response = await supabase +// .from('users') +// .select('raw_user_meta_data') +// .eq('id', userId) +// .single(); +// _setterUserDetails = response['raw_user_meta_data']; +// } catch (e) { +// print('Error fetching setter user details: $e'); +// _setterUserDetails = {'full_name': 'Unknown', 'email': 'Unknown'}; +// } +// notifyListeners(); +// } + +// void _listenToRealtimeChanges() { +// if (_deviceId == null) return; + +// _unsubscribeFromChannels(); + +// _sensorEventsChannel = supabase.channel('public:sensor_events:$_deviceId'); +// _sensorEventsChannel! +// .onPostgresChanges( +// event: PostgresChangeEvent.insert, +// schema: 'public', +// table: 'sensor_events', +// filter: PostgresChangeFilter( +// type: PostgresChangeFilterType.eq, +// column: 'device_id', +// value: _deviceId!, +// ), +// callback: (payload) { +// final newEvent = payload.newRecord; +// if (!_events.any((e) => e['id'] == newEvent['id'])) { +// _events.insert(0, newEvent); +// notifyListeners(); +// } +// }, +// ) +// .subscribe(); + +// _deviceStatusChannel = supabase.channel('public:device_status:$_deviceId'); +// _deviceStatusChannel! +// .onPostgresChanges( +// event: PostgresChangeEvent.all, +// schema: 'public', +// table: 'device_status', +// filter: PostgresChangeFilter( +// type: PostgresChangeFilterType.eq, +// column: 'device_id', +// value: _deviceId!, +// ), +// callback: (payload) async { +// final newStatus = payload.newRecord; +// _deviceStatus = newStatus; +// if (_deviceStatus != null && +// _deviceStatus!['setter_user_id'] != null) { +// await fetchSetterUserDetails(_deviceStatus!['setter_user_id']); +// } else { +// _setterUserDetails = null; +// } +// notifyListeners(); +// }, +// ) +// .subscribe(); +// } + +// Future signOut() async { +// _setLoading(true); +// await supabase.auth.signOut(); +// _setLoading(false); +// } + +// Future setSleepSchedule(int durationMicroseconds) async { +// if (_deviceId == null || _currentUser == null) { +// return "User or Device not identified."; +// } +// _setLoading(true); +// try { +// await supabase.from('device_status').update({ +// 'schedule_duration_microseconds': durationMicroseconds, +// 'setter_user_id': _currentUser!.id, +// }).eq('device_id', _deviceId!); +// _setLoading(false); +// return null; +// } on PostgrestException catch (e) { +// _setLoading(false); +// print('Error setting sleep schedule: ${e.message}'); +// return e.message; +// } catch (e) { +// _setLoading(false); +// print('Generic error setting sleep schedule: $e'); +// return 'An unexpected error occurred.'; +// } +// } +// } + +// // ================================================================= +// // SPLASH SCREEN PAGE +// // ================================================================= + +// class SplashScreen extends StatefulWidget { +// const SplashScreen({super.key}); + +// @override +// State createState() => _SplashScreenState(); +// } + +// class _SplashScreenState extends State { +// @override +// void initState() { +// super.initState(); +// Future.delayed(const Duration(seconds: 3), () { +// if (mounted) { +// Navigator.of(context).pushReplacement( +// MaterialPageRoute(builder: (_) => const AuthWrapper())); +// } +// }); +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: Center( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// const Icon(CupertinoIcons.lock_shield_fill, +// size: 120, color: Colors.tealAccent), +// const SizedBox(height: 24), +// Text( +// 'Security Monitor', +// style: Theme.of(context) +// .textTheme +// .titleLarge +// ?.copyWith(fontSize: 28), +// ), +// const SizedBox(height: 80), +// const CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation(Colors.white54), +// ), +// ], +// ), +// ), +// ); +// } +// } + +// // ================================================================= +// // AUTH WRAPPER (Gatekeeper) +// // ================================================================= + +// class AuthWrapper extends StatelessWidget { +// const AuthWrapper({super.key}); + +// @override +// Widget build(BuildContext context) { +// return Consumer( +// builder: (context, appState, child) { +// if (appState.currentUser != null && appState.deviceId != null) { +// return const DashboardPage(); +// } +// return const LoginPage(); +// }, +// ); +// } +// } + +// // ================================================================= +// // LOGIN PAGE +// // ================================================================= + +// class LoginPage extends StatefulWidget { +// const LoginPage({super.key}); + +// @override +// State createState() => _LoginPageState(); +// } + +// class _LoginPageState extends State { +// final _emailController = TextEditingController(); +// final _passwordController = TextEditingController(); +// final _deviceIdController = TextEditingController(); +// final _formKey = GlobalKey(); +// bool _isLoading = false; + +// Future _signIn() async { +// if (_formKey.currentState!.validate()) { +// setState(() => _isLoading = true); +// try { +// final authResponse = await supabase.auth.signInWithPassword( +// email: _emailController.text.trim(), +// password: _passwordController.text.trim(), +// ); +// if (authResponse.user != null) { +// if (!mounted) return; +// await Provider.of(context, listen: false) +// .setDeviceIdAndFetchData(_deviceIdController.text.trim()); +// } +// } on AuthException catch (e) { +// if (!mounted) return; +// ScaffoldMessenger.of(context).showSnackBar(SnackBar( +// content: Text(e.message), +// backgroundColor: Colors.redAccent, +// )); +// } catch (e) { +// if (!mounted) return; +// ScaffoldMessenger.of(context).showSnackBar(const SnackBar( +// content: Text('An unexpected error occurred.'), +// backgroundColor: Colors.redAccent, +// )); +// } finally { +// if (mounted) { +// setState(() => _isLoading = false); +// } +// } +// } +// } + +// @override +// void dispose() { +// _emailController.dispose(); +// _passwordController.dispose(); +// _deviceIdController.dispose(); +// super.dispose(); +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: Center( +// child: SingleChildScrollView( +// padding: const EdgeInsets.all(24.0), +// child: Form( +// key: _formKey, +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// crossAxisAlignment: CrossAxisAlignment.stretch, +// children: [ +// const Icon(CupertinoIcons.shield_lefthalf_fill, +// size: 80, color: Colors.tealAccent), +// const SizedBox(height: 16), +// const Text('Security Monitor', +// textAlign: TextAlign.center, +// style: +// TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), +// const SizedBox(height: 48), +// TextFormField( +// controller: _emailController, +// decoration: const InputDecoration(labelText: 'Email'), +// keyboardType: TextInputType.emailAddress, +// validator: (value) => +// value!.isEmpty ? 'Email tidak boleh kosong' : null, +// ), +// const SizedBox(height: 16), +// TextFormField( +// controller: _passwordController, +// decoration: const InputDecoration(labelText: 'Password'), +// obscureText: true, +// validator: (value) => +// value!.isEmpty ? 'Password tidak boleh kosong' : null, +// ), +// const SizedBox(height: 16), +// TextFormField( +// controller: _deviceIdController, +// decoration: const InputDecoration(labelText: 'Device ID'), +// validator: (value) => +// value!.isEmpty ? 'Device ID tidak boleh kosong' : null, +// ), +// const SizedBox(height: 32), +// ElevatedButton( +// onPressed: _isLoading ? null : _signIn, +// style: ElevatedButton.styleFrom( +// padding: const EdgeInsets.symmetric(vertical: 16), +// backgroundColor: Colors.tealAccent, +// foregroundColor: Colors.black, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(12)), +// ), +// child: _isLoading +// ? const SizedBox( +// width: 24, +// height: 24, +// child: CircularProgressIndicator( +// strokeWidth: 2, +// color: Colors.black, +// )) +// : const Text('Login', +// style: TextStyle( +// fontSize: 16, fontWeight: FontWeight.bold)), +// ), +// const SizedBox(height: 24), +// TextButton( +// onPressed: () => Navigator.push(context, +// MaterialPageRoute(builder: (_) => const RegisterPage())), +// child: const Text('Belum punya akun? Daftar sekarang', +// style: TextStyle(color: Colors.white70)), +// ), +// ], +// ), +// ), +// ), +// ), +// ); +// } +// } + +// // ================================================================= +// // REGISTER PAGE +// // ================================================================= +// class RegisterPage extends StatefulWidget { +// const RegisterPage({super.key}); + +// @override +// State createState() => _RegisterPageState(); +// } + +// class _RegisterPageState extends State { +// final _fullNameController = TextEditingController(); +// final _emailController = TextEditingController(); +// final _passwordController = TextEditingController(); +// final _formKey = GlobalKey(); +// bool _isLoading = false; + +// Future _signUp() async { +// if (_formKey.currentState!.validate()) { +// setState(() => _isLoading = true); +// try { +// await supabase.auth.signUp( +// email: _emailController.text.trim(), +// password: _passwordController.text.trim(), +// data: {'full_name': _fullNameController.text.trim()}, +// ); +// if (!mounted) return; +// ScaffoldMessenger.of(context).showSnackBar(const SnackBar( +// content: Text( +// 'Registrasi berhasil! Silakan periksa email Anda untuk verifikasi dan kemudian login.'), +// backgroundColor: Colors.green, +// )); +// Navigator.pop(context); +// } on AuthException catch (e) { +// if (!mounted) return; +// ScaffoldMessenger.of(context).showSnackBar(SnackBar( +// content: Text(e.message), +// backgroundColor: Colors.redAccent, +// )); +// } catch (e) { +// if (!mounted) return; +// ScaffoldMessenger.of(context).showSnackBar(const SnackBar( +// content: Text('An unexpected error occurred.'), +// backgroundColor: Colors.redAccent, +// )); +// } finally { +// if (mounted) { +// setState(() => _isLoading = false); +// } +// } +// } +// } + +// @override +// void dispose() { +// _fullNameController.dispose(); +// _emailController.dispose(); +// _passwordController.dispose(); +// super.dispose(); +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// backgroundColor: Colors.transparent, +// elevation: 0, +// ), +// body: Center( +// child: SingleChildScrollView( +// padding: const EdgeInsets.all(24.0), +// child: Form( +// key: _formKey, +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// crossAxisAlignment: CrossAxisAlignment.stretch, +// children: [ +// const Icon(CupertinoIcons.person_add_solid, +// size: 80, color: Colors.tealAccent), +// const SizedBox(height: 16), +// const Text('Buat Akun Baru', +// textAlign: TextAlign.center, +// style: +// TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), +// const SizedBox(height: 48), +// TextFormField( +// controller: _fullNameController, +// decoration: const InputDecoration(labelText: 'Nama Lengkap'), +// validator: (value) => +// value!.isEmpty ? 'Nama tidak boleh kosong' : null, +// ), +// const SizedBox(height: 16), +// TextFormField( +// controller: _emailController, +// decoration: const InputDecoration(labelText: 'Email'), +// keyboardType: TextInputType.emailAddress, +// validator: (value) => +// value!.isEmpty ? 'Email tidak boleh kosong' : null, +// ), +// const SizedBox(height: 16), +// TextFormField( +// controller: _passwordController, +// decoration: const InputDecoration(labelText: 'Password'), +// obscureText: true, +// validator: (value) => +// value!.length < 6 ? 'Password minimal 6 karakter' : null, +// ), +// const SizedBox(height: 32), +// ElevatedButton( +// onPressed: _isLoading ? null : _signUp, +// style: ElevatedButton.styleFrom( +// padding: const EdgeInsets.symmetric(vertical: 16), +// backgroundColor: Colors.tealAccent, +// foregroundColor: Colors.black, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(12)), +// ), +// child: _isLoading +// ? const SizedBox( +// width: 24, +// height: 24, +// child: CircularProgressIndicator( +// strokeWidth: 2, +// color: Colors.black, +// )) +// : const Text('Register', +// style: TextStyle( +// fontSize: 16, fontWeight: FontWeight.bold)), +// ), +// ], +// ), +// ), +// ), +// ), +// ); +// } +// } + +// // ================================================================= +// // DASHBOARD PAGE +// // ================================================================= +// class DashboardPage extends StatelessWidget { +// const DashboardPage({super.key}); + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: SafeArea( +// child: Stack( +// children: [ +// Consumer( +// builder: (context, appState, child) { +// if (appState.isLoading && appState.events.isEmpty) { +// return const Center(child: CircularProgressIndicator()); +// } +// return RefreshIndicator( +// onRefresh: Provider.of(context, listen: false) +// .fetchInitialData, +// color: Colors.tealAccent, +// backgroundColor: const Color(0xFF2c2c2c), +// child: const CustomScrollView( +// slivers: [ +// SliverToBoxAdapter(child: ProfileBar()), +// SliverToBoxAdapter(child: SettingsBar()), +// SliverToBoxAdapter(child: SizedBox(height: 20)), +// SliverToBoxAdapter( +// child: Padding( +// padding: EdgeInsets.symmetric(horizontal: 16.0), +// child: Text("Footage Gallery", +// style: TextStyle( +// fontSize: 22, fontWeight: FontWeight.bold)), +// )), +// SliverToBoxAdapter(child: SizedBox(height: 10)), +// FootageGallery(), +// ], +// ), +// ); +// }, +// ), +// // Indikator loading linear di bagian atas +// Positioned( +// top: 0, +// left: 0, +// right: 0, +// child: Consumer( +// builder: (context, appState, child) { +// return Visibility( +// visible: appState.isLoading, +// child: const LinearProgressIndicator( +// color: Colors.tealAccent, +// backgroundColor: Colors.transparent, +// ), +// ); +// }, +// ), +// ) +// ], +// ), +// ), +// ); +// } +// } + +// // ================================================================= +// // DASHBOARD COMPONENTS: PROFILE BAR +// // ================================================================= +// class ProfileBar extends StatelessWidget { +// const ProfileBar({super.key}); + +// @override +// Widget build(BuildContext context) { +// final appState = Provider.of(context); +// final user = appState.currentUser; +// final fullName = user?.userMetadata?['full_name'] ?? 'No Name'; +// final email = user?.email ?? 'No Email'; + +// return Padding( +// padding: const EdgeInsets.all(16.0), +// child: Row( +// children: [ +// CircleAvatar( +// backgroundColor: Colors.teal, +// child: Text(fullName.isNotEmpty ? fullName[0].toUpperCase() : 'U', +// style: const TextStyle( +// color: Colors.white, fontWeight: FontWeight.bold)), +// ), +// const SizedBox(width: 12), +// Expanded( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text(fullName, +// style: const TextStyle( +// fontSize: 18, +// fontWeight: FontWeight.bold, +// color: Colors.white)), +// Text(email, +// style: +// const TextStyle(fontSize: 14, color: Colors.white70)), +// ], +// ), +// ), +// IconButton( +// tooltip: "Logout", +// icon: const Icon(Icons.logout, color: Colors.white70), +// onPressed: () { +// Provider.of(context, listen: false).signOut(); +// }, +// ) +// ], +// ), +// ); +// } +// } + +// // ================================================================= +// // DASHBOARD COMPONENTS: SETTINGS BAR +// // ================================================================= +// class SettingsBar extends StatelessWidget { +// const SettingsBar({super.key}); + +// void _showSetTimerDialog(BuildContext context, AppState appState) async { +// final now = DateTime.now(); +// final TimeOfDay? pickedTime = await showTimePicker( +// context: context, +// initialTime: TimeOfDay.fromDateTime(now.add(const Duration(hours: 1))), +// ); + +// if (pickedTime != null) { +// DateTime scheduledTime = DateTime( +// now.year, now.month, now.day, pickedTime.hour, pickedTime.minute); +// if (scheduledTime.isBefore(now)) { +// scheduledTime = scheduledTime.add(const Duration(days: 1)); +// } + +// final duration = scheduledTime.difference(now); +// final durationMicroseconds = duration.inMicroseconds; + +// final error = await appState.setSleepSchedule(durationMicroseconds); + +// if (context.mounted) { +// if (error == null) { +// // PERBAIKAN: Menggunakan DateFormat yang sudah diinisialisasi +// final formattedTime = DateFormat.jm('id_ID').format(scheduledTime); +// ScaffoldMessenger.of(context).showSnackBar(SnackBar( +// content: Text('Jadwal deep sleep diatur hingga $formattedTime'), +// backgroundColor: Colors.green, +// )); +// } else { +// ScaffoldMessenger.of(context).showSnackBar(SnackBar( +// content: Text('Gagal mengatur jadwal: $error'), +// backgroundColor: Colors.redAccent, +// )); +// } +// } +// } +// } + +// @override +// Widget build(BuildContext context) { +// return Consumer( +// builder: (context, appState, child) { +// final status = appState.deviceStatus; +// if (status == null) { +// return const Padding( +// padding: EdgeInsets.symmetric(horizontal: 16.0), +// child: Center( +// child: Text("Mencari status perangkat...", +// style: TextStyle(color: Colors.white24))), +// ); +// } + +// final bool isOnline = status['is_online'] ?? false; +// final String deviceId = status['device_id'] ?? 'N/A'; +// final setterName = appState.setterUserDetails?['full_name'] ?? 'N/A'; +// final setterEmail = +// appState.setterUserDetails?['email'] ?? 'Belum pernah diatur'; + +// return Padding( +// padding: const EdgeInsets.symmetric(horizontal: 16.0), +// child: Container( +// padding: const EdgeInsets.all(16), +// decoration: BoxDecoration( +// color: Theme.of(context).cardColor, +// borderRadius: BorderRadius.circular(16), +// ), +// child: Row( +// children: [ +// Expanded( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text('Device: $deviceId', +// style: const TextStyle( +// fontSize: 16, fontWeight: FontWeight.bold)), +// const SizedBox(height: 8), +// Row( +// children: [ +// Icon(Icons.circle, +// color: isOnline +// ? Colors.greenAccent +// : Colors.redAccent, +// size: 12), +// const SizedBox(width: 8), +// Text(isOnline ? 'Online' : 'Offline', +// style: const TextStyle(color: Colors.white)), +// ], +// ), +// const SizedBox(height: 8), +// Text( +// 'Last Setter: $setterName ($setterEmail)', +// style: const TextStyle( +// fontSize: 12, color: Colors.white54), +// overflow: TextOverflow.ellipsis, +// ), +// ], +// ), +// ), +// const SizedBox(width: 12), +// ElevatedButton( +// onPressed: isOnline +// ? () => _showSetTimerDialog(context, appState) +// : null, +// style: ElevatedButton.styleFrom( +// backgroundColor: Colors.teal, +// foregroundColor: Colors.white, +// disabledBackgroundColor: Colors.grey.withOpacity(0.3)), +// child: const Text('Set Timer'), +// ) +// ], +// ), +// ), +// ); +// }, +// ); +// } +// } + +// // ================================================================= +// // DASHBOARD COMPONENTS: FOOTAGE GALLERY +// // ================================================================= + +// class FootageGallery extends StatelessWidget { +// const FootageGallery({super.key}); + +// @override +// Widget build(BuildContext context) { +// return Consumer( +// builder: (context, appState, child) { +// final events = appState.events; +// if (events.isEmpty) { +// return const SliverToBoxAdapter( +// child: Center( +// child: Padding( +// padding: EdgeInsets.all(48.0), +// child: Column( +// children: [ +// Icon(CupertinoIcons.photo_on_rectangle, +// size: 60, color: Colors.white24), +// SizedBox(height: 16), +// Text('Belum ada rekaman', +// style: TextStyle(color: Colors.white24, fontSize: 16)), +// ], +// ), +// ), +// ), +// ); +// } +// return SliverList( +// delegate: SliverChildBuilderDelegate( +// (context, index) { +// final event = events[index]; +// return FootageCard(event: event); +// }, +// childCount: events.length, +// ), +// ); +// }, +// ); +// } +// } + +// class FootageCard extends StatelessWidget { +// final Map event; +// const FootageCard({super.key, required this.event}); + +// @override +// Widget build(BuildContext context) { +// final imageUrl = event['image_ref'] ?? ''; +// final location = event['location_name'] ?? 'Unknown Location'; +// final eventType = event['event_type'] ?? 'Unknown Event'; +// final timestamp = DateTime.parse(event['event_timestamp']); + +// // ================================================================= +// // PERBAIKAN: Menggunakan DateFormat langsung di sini. +// // Locale 'id_ID' sudah dijamin siap karena inisialisasi di main(). +// // Format tahun (yyyy) juga sudah diperbaiki. +// // ================================================================= +// final formattedTime = DateFormat('EEEE, dd MMMM yyyy, HH:mm:ss', 'id_ID') +// .format(timestamp.toLocal()); + +// return Container( +// margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), +// decoration: BoxDecoration( +// color: Theme.of(context).cardColor, +// borderRadius: BorderRadius.circular(16), +// boxShadow: [ +// BoxShadow( +// color: Colors.black.withOpacity(0.5), +// spreadRadius: 2, +// blurRadius: 10, +// offset: const Offset(0, 4), +// ) +// ], +// ), +// child: ClipRRect( +// borderRadius: BorderRadius.circular(16), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// if (imageUrl.isNotEmpty) +// Image.network( +// imageUrl, +// fit: BoxFit.cover, +// width: double.infinity, +// height: 250, +// loadingBuilder: (context, child, loadingProgress) { +// if (loadingProgress == null) return child; +// return Container( +// height: 250, +// color: Colors.black12, +// child: Center( +// child: CircularProgressIndicator( +// color: Colors.tealAccent, +// value: loadingProgress.expectedTotalBytes != null +// ? loadingProgress.cumulativeBytesLoaded / +// loadingProgress.expectedTotalBytes! +// : null, +// ), +// ), +// ); +// }, +// errorBuilder: (context, error, stackTrace) => Container( +// height: 250, +// color: Colors.black12, +// child: const Center( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Icon(CupertinoIcons.exclamationmark_triangle, +// color: Colors.redAccent, size: 50), +// SizedBox(height: 8), +// Text("Gagal memuat gambar", +// style: TextStyle(color: Colors.white70)) +// ], +// ), +// ), +// ), +// ), +// Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Container( +// padding: +// const EdgeInsets.symmetric(horizontal: 8, vertical: 4), +// decoration: BoxDecoration( +// color: eventType == 'GERAKAN' +// ? Colors.orange.shade800 +// : Colors.purple.shade800, +// borderRadius: BorderRadius.circular(8)), +// child: Text( +// eventType, +// style: const TextStyle( +// color: Colors.white, fontWeight: FontWeight.bold), +// ), +// ), +// const SizedBox(height: 12), +// Row( +// children: [ +// const Icon(CupertinoIcons.location_solid, +// size: 16, color: Colors.white70), +// const SizedBox(width: 8), +// Text(location, +// style: const TextStyle( +// fontSize: 16, color: Colors.white)), +// ], +// ), +// const SizedBox(height: 8), +// Row( +// children: [ +// const Icon(CupertinoIcons.time_solid, +// size: 16, color: Colors.white70), +// const SizedBox(width: 8), +// Text(formattedTime, +// style: const TextStyle( +// fontSize: 14, color: Colors.white70)), +// ], +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/newata2/lib/main copy 5.dart b/newata2/lib/main copy 5.dart new file mode 100644 index 0000000..e33eb40 --- /dev/null +++ b/newata2/lib/main copy 5.dart @@ -0,0 +1,1051 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +// ================================================================= +// MAIN.DART & SETUP +// ================================================================= + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(fileName: ".env"); + + await Supabase.initialize( + url: dotenv.env['SUPABASE_URL']!, + anonKey: dotenv.env['SUPABASE_ANON_KEY']!, + ); + + await initializeDateFormatting('id_ID', null); + + runApp( + ChangeNotifierProvider( + create: (context) => AppState(), + child: const MyApp(), + ), + ); +} + +final supabase = Supabase.instance.client; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Security Monitor', + theme: ThemeData( + brightness: Brightness.dark, + primaryColor: Colors.tealAccent, + scaffoldBackgroundColor: const Color(0xFF1a1a1a), + cardColor: const Color(0xFF2c2c2c), + colorScheme: const ColorScheme.dark( + primary: Colors.tealAccent, + secondary: Colors.teal, + surface: Color(0xFF2c2c2c), + onSurface: Colors.white, + ), + textTheme: const TextTheme( + bodyLarge: TextStyle(color: Colors.white70), + bodyMedium: TextStyle(color: Colors.white70), + titleLarge: + TextStyle(color: Colors.white, fontWeight: FontWeight.bold), + titleMedium: TextStyle(color: Colors.white), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: Colors.black.withOpacity(0.3), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none), + labelStyle: const TextStyle(color: Colors.white54)), + useMaterial3: true, + ), + home: const AuthWrapper(), // Menggunakan AuthWrapper untuk logika awal + debugShowCheckedModeBanner: false, + ); + } +} + +// ================================================================= +// STATE MANAGEMENT (PROVIDER) +// ================================================================= + +class AppState extends ChangeNotifier { + bool _isLoading = false; + String? _deviceId; + List> _events = []; + Map? _deviceStatus; + User? _currentUser; + + RealtimeChannel? _eventsChannel; + RealtimeChannel? _deviceStatusChannel; + + bool get isLoading => _isLoading; + String? get deviceId => _deviceId; + List> get events => _events; + Map? get deviceStatus => _deviceStatus; + User? get currentUser => _currentUser; + + AppState() { + _currentUser = supabase.auth.currentUser; + supabase.auth.onAuthStateChange.listen((data) { + final session = data.session; + _currentUser = session?.user; + if (_currentUser == null) { + clearState(); + } + notifyListeners(); + }); + } + + void _unsubscribeFromChannels() { + if (_eventsChannel != null) { + supabase.removeChannel(_eventsChannel!); + _eventsChannel = null; + } + if (_deviceStatusChannel != null) { + supabase.removeChannel(_deviceStatusChannel!); + _deviceStatusChannel = null; + } + } + + void clearState() { + _deviceId = null; + _events.clear(); + _deviceStatus = null; + _unsubscribeFromChannels(); + notifyListeners(); + } + + void _setLoading(bool value) { + _isLoading = value; + notifyListeners(); + } + + Future setDeviceIdAndFetchData(String deviceId) async { + _setLoading(true); + try { + final response = await supabase + .from('device_status') + .select('device_id') + .eq('device_id', deviceId) + .maybeSingle(); + + if (response == null) { + _setLoading(false); + return false; + } + + _deviceId = deviceId; + await fetchInitialData(); + _listenToRealtimeChanges(); + notifyListeners(); + _setLoading(false); + return true; + } catch (e) { + print('Error validating device ID: $e'); + _setLoading(false); + return false; + } + } + + Future fetchInitialData() async { + if (_deviceId == null) return; + _setLoading(true); + await Future.wait([ + fetchEvents(), + fetchDeviceStatus(), + ]); + _setLoading(false); + } + + Future fetchEvents() async { + if (_deviceId == null) return; + try { + final response = await supabase + .from('events') + .select() + .eq('device_id', _deviceId!) + .order('timestamp', ascending: false); + _events = List>.from(response); + } catch (e) { + print('Error fetching events: $e'); + _events = []; + } + notifyListeners(); + } + + Future fetchDeviceStatus() async { + if (_deviceId == null) return; + try { + final response = await supabase + .from('device_status') + .select() + .eq('device_id', _deviceId!) + .single(); + _deviceStatus = response; + } catch (e) { + print('Error fetching device status: $e'); + _deviceStatus = null; + } + notifyListeners(); + } + + void _listenToRealtimeChanges() { + if (_deviceId == null) return; + _unsubscribeFromChannels(); + + _eventsChannel = supabase.channel('public:events:device_id=$_deviceId'); + _eventsChannel! + .onPostgresChanges( + event: PostgresChangeEvent.insert, + schema: 'public', + table: 'events', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'device_id', + value: _deviceId!, + ), + callback: (payload) { + final newEvent = payload.newRecord; + if (!_events.any((e) => e['id'] == newEvent['id'])) { + _events.insert(0, newEvent); + notifyListeners(); + } + }, + ) + .subscribe(); + + _deviceStatusChannel = + supabase.channel('public:device_status:device_id=$_deviceId'); + _deviceStatusChannel! + .onPostgresChanges( + event: PostgresChangeEvent.update, + schema: 'public', + table: 'device_status', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'device_id', + value: _deviceId!, + ), + callback: (payload) { + _deviceStatus = payload.newRecord; + notifyListeners(); + }, + ) + .subscribe(); + } + + Future signOut() async { + _setLoading(true); + await supabase.auth.signOut(); + // clearState() akan dipanggil oleh listener onAuthStateChange + _setLoading(false); + } + + Future setSleepSchedule(int durationMicroseconds) async { + if (_deviceId == null) { + return "Device not identified."; + } + _setLoading(true); + + try { + await supabase.from('device_status').update({ + 'schedule_duration_microseconds': durationMicroseconds, + 'last_update': DateTime.now().toIso8601String(), + }).eq('device_id', _deviceId!); + + _setLoading(false); + return null; + } on PostgrestException catch (e) { + _setLoading(false); + print('Error setting sleep schedule: ${e.message}'); + return e.message; + } catch (e) { + _setLoading(false); + print('Generic error setting sleep schedule: $e'); + return 'An unexpected error occurred.'; + } + } +} + +// ================================================================= +// AUTH & ROUTING +// ================================================================= + +class AuthWrapper extends StatelessWidget { + const AuthWrapper({super.key}); + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: supabase.auth.onAuthStateChange, + builder: (context, snapshot) { + // Tampilkan loading spinner selagi menunggu event auth pertama. + if (!snapshot.hasData) { + return const Scaffold( + body: Center(child: CircularProgressIndicator()), + ); + } + + // Sesuai alur aplikasi, selalu mulai dari LoginPage untuk meminta Device ID, + // terlepas dari status sesi sebelumnya. + return const LoginPage(); + }, + ); + } +} + +class RegisterPage extends StatefulWidget { + const RegisterPage({super.key}); + @override + State createState() => _RegisterPageState(); +} + +class _RegisterPageState extends State { + final _fullNameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + Future _signUp() async { + if (!_formKey.currentState!.validate()) return; + + setState(() => _isLoading = true); + try { + await supabase.auth.signUp( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + data: {'full_name': _fullNameController.text.trim()}, + ); + if (!mounted) return; + + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Registrasi berhasil! Silakan periksa email untuk verifikasi & login.'), + backgroundColor: Colors.green, + )); + Navigator.pop(context); + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("Error: ${e.toString()}"), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(backgroundColor: Colors.transparent, elevation: 0), + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.person_add_solid, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Buat Akun Baru', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _fullNameController, + decoration: const InputDecoration(labelText: 'Nama Lengkap'), + validator: (value) => + value!.isEmpty ? 'Nama tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.length < 6 ? 'Password minimal 6 karakter' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signUp, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.black)) + : const Text('Register', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + ], + ), + ), + ), + ), + ); + } +} + +class LoginPage extends StatefulWidget { + final bool showLogoutSuccess; + + const LoginPage({super.key, this.showLogoutSuccess = false}); + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _deviceIdController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + // PERBAIKAN: Menambahkan initState untuk menampilkan snackbar jika diperlukan. + @override + void initState() { + super.initState(); + if (widget.showLogoutSuccess) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Anda telah berhasil logout.'), + backgroundColor: Colors.green, + behavior: SnackBarBehavior.floating, + ), + ); + } + }); + } + } + + Future _signIn() async { + if (!_formKey.currentState!.validate()) return; + + setState(() => _isLoading = true); + final appState = Provider.of(context, listen: false); + + try { + final authResponse = await supabase.auth.signInWithPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + + if (authResponse.user == null) { + throw 'Login gagal, pengguna tidak ditemukan.'; + } + + final deviceIdIsValid = await appState + .setDeviceIdAndFetchData(_deviceIdController.text.trim()); + + if (!mounted) return; + + if (deviceIdIsValid) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const DashboardPage()), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Login berhasil, tetapi Device ID tidak terdaftar.'), + backgroundColor: Colors.orange, + )); + await appState.signOut(); + } + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Terjadi kesalahan: ${e.toString()}'), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + final userEmail = supabase.auth.currentUser?.email; + if (userEmail != null && _emailController.text.isEmpty) { + _emailController.text = userEmail; + } + + return Scaffold( + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.shield_lefthalf_fill, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Security Monitor', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.isEmpty ? 'Password tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _deviceIdController, + decoration: const InputDecoration(labelText: 'Device ID'), + validator: (value) => + value!.isEmpty ? 'Device ID tidak boleh kosong' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signIn, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.black)) + : const Text('Login', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + const SizedBox(height: 24), + TextButton( + onPressed: () => Navigator.push(context, + MaterialPageRoute(builder: (_) => const RegisterPage())), + child: const Text('Belum punya akun? Daftar sekarang', + style: TextStyle(color: Colors.white70)), + ), + ], + ), + ), + ), + ), + ); + } +} + +// ================================================================= +// DASHBOARD PAGE & COMPONENTS +// ================================================================= + +class DashboardPage extends StatelessWidget { + const DashboardPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + Consumer( + builder: (context, appState, child) { + if (appState.isLoading && appState.events.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return RefreshIndicator( + onRefresh: Provider.of(context, listen: false) + .fetchInitialData, + color: Colors.tealAccent, + backgroundColor: const Color(0xFF2c2c2c), + child: const CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: ProfileBar()), + SliverToBoxAdapter(child: SettingsBar()), + SliverToBoxAdapter(child: SizedBox(height: 20)), + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text("Footage Gallery", + style: TextStyle( + fontSize: 22, fontWeight: FontWeight.bold)), + )), + SliverToBoxAdapter(child: SizedBox(height: 10)), + FootageGallery(), + ], + ), + ); + }, + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: Consumer( + builder: (context, appState, child) { + return Visibility( + visible: appState.isLoading, + child: const LinearProgressIndicator( + color: Colors.tealAccent, + backgroundColor: Colors.transparent), + ); + }, + ), + ) + ], + ), + ), + ); + } +} + +class ProfileBar extends StatelessWidget { + const ProfileBar({super.key}); + + @override + Widget build(BuildContext context) { + final user = Provider.of(context, listen: false).currentUser; + final fullName = user?.userMetadata?['full_name'] ?? 'No Name'; + final email = user?.email ?? 'No Email'; + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + CircleAvatar( + backgroundColor: Colors.teal, + child: Text(fullName.isNotEmpty ? fullName[0].toUpperCase() : 'U', + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold)), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(fullName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white)), + Text(email, + style: + const TextStyle(fontSize: 14, color: Colors.white70)), + ], + ), + ), + IconButton( + tooltip: "Logout", + icon: const Icon(Icons.logout, color: Colors.white70), + onPressed: () async { + final bool? confirmLogout = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: const Color(0xFF2c2c2c), + title: const Text('Konfirmasi Logout', + style: TextStyle(color: Colors.white)), + content: const Text('Apakah Anda yakin ingin keluar?', + style: TextStyle(color: Colors.white70)), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Batal', + style: TextStyle(color: Colors.white70)), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Logout', + style: TextStyle(color: Colors.redAccent)), + ), + ], + ); + }, + ); + + if (!context.mounted) return; + + if (confirmLogout == true) { + // PERBAIKAN: Navigasi dulu, baru proses logout di latar belakang. + final appState = Provider.of(context, listen: false); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => + const LoginPage(showLogoutSuccess: true)), + (route) => false, + ); + + await appState.signOut(); + } + }, + ) + ], + ), + ); + } +} + +class SettingsBar extends StatelessWidget { + const SettingsBar({super.key}); + + void _showSetTimerDialog(BuildContext context, AppState appState) async { + final now = DateTime.now(); + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(now.add(const Duration(hours: 1))), + ); + + if (pickedTime != null) { + DateTime scheduledTime = DateTime( + now.year, now.month, now.day, pickedTime.hour, pickedTime.minute); + if (scheduledTime.isBefore(now)) { + scheduledTime = scheduledTime.add(const Duration(days: 1)); + } + + final duration = scheduledTime.difference(now); + final durationMicroseconds = duration.inMicroseconds; + + final error = await appState.setSleepSchedule(durationMicroseconds); + + if (context.mounted) { + if (error == null) { + final formattedTime = DateFormat.jm('id_ID').format(scheduledTime); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Jadwal deep sleep diatur hingga $formattedTime'), + backgroundColor: Colors.green, + )); + } else { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Gagal mengatur jadwal: $error'), + backgroundColor: Colors.redAccent, + )); + } + } + } + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + final statusData = appState.deviceStatus; + + if (statusData == null) { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Center( + child: Text("Mencari status perangkat...", + style: TextStyle(color: Colors.white24))), + ); + } + + final deviceId = statusData['device_id'] ?? 'N/A'; + final deviceStatus = statusData['status'] ?? 'unknown'; + final isOnline = deviceStatus == 'active'; + final lastUpdateString = + statusData['last_update'] ?? DateTime.now().toIso8601String(); + final lastUpdate = DateTime.parse(lastUpdateString).toLocal(); + final formattedLastUpdate = + DateFormat('dd MMM, HH:mm:ss', 'id_ID').format(lastUpdate); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Device: $deviceId', + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + Row( + children: [ + Icon(Icons.circle, + color: isOnline + ? Colors.greenAccent + : Colors.redAccent, + size: 12), + const SizedBox(width: 8), + Text(isOnline ? 'Active' : 'Inactive', + style: const TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 8), + Text( + 'Last update: $formattedLastUpdate', + style: const TextStyle( + fontSize: 12, color: Colors.white54), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + const SizedBox(width: 12), + ElevatedButton( + onPressed: isOnline + ? () => _showSetTimerDialog(context, appState) + : null, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + foregroundColor: Colors.white, + disabledBackgroundColor: Colors.grey.withOpacity(0.3)), + child: const Text('Set Timer'), + ) + ], + ), + ), + ); + }, + ); + } +} + +class FootageGallery extends StatelessWidget { + const FootageGallery({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, appState, child) { + final events = appState.events; + if (events.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(48.0), + child: Column( + children: [ + Icon(CupertinoIcons.photo_on_rectangle, + size: 60, color: Colors.white24), + SizedBox(height: 16), + Text('Belum ada tangkapan', + style: TextStyle(color: Colors.white24, fontSize: 16)), + ], + ), + ), + ), + ); + } + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final event = events[index]; + return FootageCard(event: event); + }, + childCount: events.length, + ), + ); + }, + ); + } +} + +class FootageCard extends StatelessWidget { + final Map event; + const FootageCard({super.key, required this.event}); + + @override + Widget build(BuildContext context) { + final imageUrl = event['image_ref'] as String?; + final location = event['location'] ?? 'Unknown Location'; + final eventType = event['event_type'] ?? 'Unknown Event'; + final timestampString = + event['timestamp'] ?? DateTime.now().toIso8601String(); + final timestamp = DateTime.parse(timestampString); + + final formattedTime = DateFormat('EEEE, dd MMMM yyyy, HH:mm:ss', 'id_ID') + .format(timestamp.toLocal()); + + String displayEventType = 'Unknown'; + Color eventColor = Colors.grey; + if (eventType == 'motion') { + displayEventType = 'Motion Detected'; + eventColor = Colors.orange.shade800; + } else if (eventType == 'vibration') { + displayEventType = 'Vibration Detected'; + eventColor = Colors.purple.shade800; + } + + return Container( + margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 4), + ) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (imageUrl != null && imageUrl.isNotEmpty) + Image.network( + imageUrl, + fit: BoxFit.cover, + width: double.infinity, + height: 250, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + height: 250, + color: Colors.black12, + child: Center( + child: CircularProgressIndicator( + color: Colors.tealAccent, + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) => Container( + height: 250, + color: Colors.black12, + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.exclamationmark_triangle, + color: Colors.redAccent, size: 50), + SizedBox(height: 8), + Text("Gagal memuat gambar", + style: TextStyle(color: Colors.white70)) + ], + ), + ), + ), + ) + else + Container( + height: 100, + color: Colors.black12, + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(CupertinoIcons.videocam, + size: 30, color: Colors.white24), + SizedBox(height: 8), + Text("No image captured", + style: TextStyle(color: Colors.white24)) + ], + )), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: eventColor, + borderRadius: BorderRadius.circular(8)), + child: Text( + displayEventType, + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ), + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(CupertinoIcons.location_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(location, + style: const TextStyle( + fontSize: 16, color: Colors.white)), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon(CupertinoIcons.time_solid, + size: 16, color: Colors.white70), + const SizedBox(width: 8), + Text(formattedTime, + style: const TextStyle( + fontSize: 14, color: Colors.white70)), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/newata2/lib/main.dart b/newata2/lib/main.dart index a4ae449..fc0e793 100644 --- a/newata2/lib/main.dart +++ b/newata2/lib/main.dart @@ -1,63 +1,32 @@ -// main.dart +import 'package:CCTV_App/auth_wrapper.dart'; +import 'package:CCTV_App/provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:intl/date_symbol_data_local.dart'; import 'package:provider/provider.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; -import 'package:intl/date_symbol_data_local.dart'; -// Providers -import 'providers/auth_provider.dart'; -import 'providers/pantau_footage_provider.dart'; -import 'providers/schedule_provider.dart'; - -// Screens -import 'screens/splash_screen.dart'; -import 'screens/login_screen.dart'; -import 'screens/register_screen.dart'; -import 'screens/dashboard_screen.dart'; -import 'screens/video_player_screen.dart'; // Pastikan path benar -import 'screens/image_viewer_screen.dart'; // Pastikan path benar +// ================================================================= +// MAIN.DART & SETUP +// ================================================================= Future main() async { WidgetsFlutterBinding.ensureInitialized(); - print("main: WidgetsFlutterBinding initialized."); // Log + await dotenv.load(fileName: ".env"); - try { - await dotenv.load(fileName: ".env"); - print("main: .env file loaded successfully."); // Log - } catch (e) { - print("main: Error loading .env file: $e"); // Log error - } + await Supabase.initialize( + url: dotenv.env['SUPABASE_URL']!, + anonKey: dotenv.env['SUPABASE_ANON_KEY']!, + ); - final supabaseUrl = dotenv.env['SUPABASE_URL']; - final supabaseAnonKey = dotenv.env['SUPABASE_ANON_KEY']; + await initializeDateFormatting('id_ID', null); - if (supabaseUrl == null || supabaseAnonKey == null) { - print( - "main: Supabase URL or Anon Key not found in .env file. App cannot initialize Supabase."); // Log error - return; - } - print("main: Supabase URL and Anon Key found."); // Log - - try { - await Supabase.initialize( - url: supabaseUrl, - anonKey: supabaseAnonKey, - ); - print("main: Supabase initialized successfully."); // Log - } catch (e) { - print("main: Error initializing Supabase: $e"); // Log error - return; - } - - try { - await initializeDateFormatting('id_ID', null); - print("main: Date formatting initialized."); // Log - } catch (e) { - print("main: Error initializing date formatting: $e"); // Log error - } - - runApp(const MyApp()); + runApp( + ChangeNotifierProvider( + create: (context) => AppState(), + child: const MyApp(), + ), + ); } final supabase = Supabase.instance.client; @@ -67,137 +36,42 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - print("MyApp: Building..."); // Log - return MultiProvider( - providers: [ - ChangeNotifierProvider(create: (_) => AuthProvider()), - ChangeNotifierProxyProvider( - create: (_) => FootageProvider(null), - update: (_, auth, previousFootage) => - FootageProvider(auth.currentUser?.id), + return MaterialApp( + title: 'Security Monitor', + theme: ThemeData( + brightness: Brightness.dark, + primaryColor: Colors.tealAccent, + scaffoldBackgroundColor: const Color(0xFF1a1a1a), + cardColor: const Color(0xFF2c2c2c), + colorScheme: const ColorScheme.dark( + primary: Colors.tealAccent, + secondary: Colors.teal, + surface: Color(0xFF2c2c2c), + onSurface: Colors.white, ), - ChangeNotifierProxyProvider( - create: (_) => ScheduleProvider(null), - update: (_, auth, previousSchedule) => - ScheduleProvider(auth.currentUser?.id), + textTheme: const TextTheme( + bodyLarge: TextStyle(color: Colors.white70), + bodyMedium: TextStyle(color: Colors.white70), + titleLarge: + TextStyle(color: Colors.white, fontWeight: FontWeight.bold), + titleMedium: TextStyle(color: Colors.white), ), - ], - child: MaterialApp( - title: 'CCTV IoT App', - theme: ThemeData( - // Tema tetap sama - primarySwatch: Colors.teal, - visualDensity: VisualDensity.adaptivePlatformDensity, - fontFamily: 'Inter', - scaffoldBackgroundColor: Colors.grey[100], - appBarTheme: AppBarTheme( - backgroundColor: Colors.teal[600], - elevation: 2, - iconTheme: - const IconThemeData(color: Colors.white), // Warna ikon AppBar - titleTextStyle: const TextStyle( - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.w500) // Style title AppBar - ), - bottomNavigationBarTheme: BottomNavigationBarThemeData( - selectedItemColor: Colors.teal[700], - unselectedItemColor: Colors.grey[600], - ), - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.teal[500], - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), - )), - cardTheme: CardTheme( - elevation: 1, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), - ), - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: Colors.grey[400]!), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Colors.teal), - ), + inputDecorationTheme: InputDecorationTheme( filled: true, - fillColor: Colors.white, - contentPadding: - const EdgeInsets.symmetric(vertical: 14.0, horizontal: 12.0), - ), - listTileTheme: ListTileThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom( - foregroundColor: Colors.teal[600], - ), - ), - dialogTheme: DialogTheme( - // Style untuk dialog (misal loading) - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - elevation: 5, - ), - timePickerTheme: TimePickerThemeData( - // Style untuk TimePicker - backgroundColor: Colors.white, - hourMinuteShape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(8)), - side: BorderSide(color: Colors.grey[300]!, width: 1), - ), - dayPeriodShape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(8)), - side: BorderSide(color: Colors.grey[300]!, width: 1), - ), - dayPeriodColor: Colors.teal[50], - dayPeriodTextColor: Colors.teal[800], - hourMinuteColor: WidgetStateColor.resolveWith((states) => - states.contains(WidgetState.selected) - ? Colors.teal[100]! - : Colors.grey[100]!), - hourMinuteTextColor: WidgetStateColor.resolveWith((states) => - states.contains(WidgetState.selected) - ? Colors.teal[900]! - : Colors.black54), - dialHandColor: Colors.teal[300], - dialBackgroundColor: Colors.teal[50], - dialTextColor: WidgetStateColor.resolveWith((states) => - states.contains(WidgetState.selected) - ? Colors.white - : Colors.teal[900]!), - entryModeIconColor: Colors.teal[600], - helpTextStyle: TextStyle(color: Colors.teal[800]), - ), - ), - debugShowCheckedModeBanner: false, - home: SplashScreen(), - routes: { - '/login': (context) => const LoginScreen(), - '/register': (context) => const RegisterScreen(), - '/dashboard': (context) => const DashboardScreen(), - '/video_player': (context) => VideoPlayerScreen( - videoUrl: - ModalRoute.of(context)?.settings.arguments as String? ?? '', - ), - '/image_viewer': (context) => ImageViewerScreen( - imageUrl: - ModalRoute.of(context)?.settings.arguments as String? ?? '', - ), - }, + fillColor: Colors.black.withOpacity(0.3), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none), + labelStyle: const TextStyle(color: Colors.white54)), + useMaterial3: true, ), + home: const AuthWrapper(), // Menggunakan AuthWrapper untuk logika awal + debugShowCheckedModeBanner: false, ); } } + + +// SUPABASE_URL=https://ihuetazxejsioxcjejpj.supabase.co +// SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlodWV0YXp4ZWpzaW94Y2planBqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTExMDEyMTgsImV4cCI6MjA2NjY3NzIxOH0.IKFyrKioiScfAS_9UouPEcesHHoas0SDZH0mZBCA1ro + diff --git a/newata2/lib/provider.dart b/newata2/lib/provider.dart new file mode 100644 index 0000000..ef53d09 --- /dev/null +++ b/newata2/lib/provider.dart @@ -0,0 +1,206 @@ +// ================================================================= +// STATE MANAGEMENT (PROVIDER) +// ================================================================= + +import 'package:CCTV_App/main.dart'; +import 'package:flutter/material.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +class AppState extends ChangeNotifier { + bool _isLoading = false; + String? _deviceId; + List> _events = []; + Map? _deviceStatus; + User? _currentUser; + + RealtimeChannel? _eventsChannel; + RealtimeChannel? _deviceStatusChannel; + + bool get isLoading => _isLoading; + String? get deviceId => _deviceId; + List> get events => _events; + Map? get deviceStatus => _deviceStatus; + User? get currentUser => _currentUser; + + AppState() { + _currentUser = supabase.auth.currentUser; + supabase.auth.onAuthStateChange.listen((data) { + final session = data.session; + _currentUser = session?.user; + if (_currentUser == null) { + clearState(); + } + notifyListeners(); + }); + } + + void _unsubscribeFromChannels() { + if (_eventsChannel != null) { + supabase.removeChannel(_eventsChannel!); + _eventsChannel = null; + } + if (_deviceStatusChannel != null) { + supabase.removeChannel(_deviceStatusChannel!); + _deviceStatusChannel = null; + } + } + + void clearState() { + _deviceId = null; + _events.clear(); + _deviceStatus = null; + _unsubscribeFromChannels(); + notifyListeners(); + } + + void _setLoading(bool value) { + _isLoading = value; + notifyListeners(); + } + + Future setDeviceIdAndFetchData(String deviceId) async { + _setLoading(true); + try { + final response = await supabase + .from('device_status') + .select('device_id') + .eq('device_id', deviceId) + .maybeSingle(); + + if (response == null) { + _setLoading(false); + return false; + } + + _deviceId = deviceId; + await fetchInitialData(); + _listenToRealtimeChanges(); + notifyListeners(); + _setLoading(false); + return true; + } catch (e) { + print('Error validating device ID: $e'); + _setLoading(false); + return false; + } + } + + Future fetchInitialData() async { + if (_deviceId == null) return; + _setLoading(true); + await Future.wait([ + fetchEvents(), + fetchDeviceStatus(), + ]); + _setLoading(false); + } + + Future fetchEvents() async { + if (_deviceId == null) return; + try { + final response = await supabase + .from('events') + .select() + .eq('device_id', _deviceId!) + .order('timestamp', ascending: false); + _events = List>.from(response); + } catch (e) { + print('Error fetching events: $e'); + _events = []; + } + notifyListeners(); + } + + Future fetchDeviceStatus() async { + if (_deviceId == null) return; + try { + final response = await supabase + .from('device_status') + .select() + .eq('device_id', _deviceId!) + .single(); + _deviceStatus = response; + } catch (e) { + print('Error fetching device status: $e'); + _deviceStatus = null; + } + notifyListeners(); + } + + void _listenToRealtimeChanges() { + if (_deviceId == null) return; + _unsubscribeFromChannels(); + + _eventsChannel = supabase.channel('public:events:device_id=$_deviceId'); + _eventsChannel! + .onPostgresChanges( + event: PostgresChangeEvent.insert, + schema: 'public', + table: 'events', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'device_id', + value: _deviceId!, + ), + callback: (payload) { + final newEvent = payload.newRecord; + if (!_events.any((e) => e['id'] == newEvent['id'])) { + _events.insert(0, newEvent); + notifyListeners(); + } + }, + ) + .subscribe(); + + _deviceStatusChannel = + supabase.channel('public:device_status:device_id=$_deviceId'); + _deviceStatusChannel! + .onPostgresChanges( + event: PostgresChangeEvent.update, + schema: 'public', + table: 'device_status', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'device_id', + value: _deviceId!, + ), + callback: (payload) { + _deviceStatus = payload.newRecord; + notifyListeners(); + }, + ) + .subscribe(); + } + + Future signOut() async { + _setLoading(true); + await supabase.auth.signOut(); + // clearState() akan dipanggil oleh listener onAuthStateChange + _setLoading(false); + } + + Future setSleepSchedule(int durationMicroseconds) async { + if (_deviceId == null) { + return "Device not identified."; + } + _setLoading(true); + + try { + await supabase.from('device_status').update({ + 'schedule_duration_microseconds': durationMicroseconds, + 'last_update': DateTime.now().toIso8601String(), + }).eq('device_id', _deviceId!); + + _setLoading(false); + return null; + } on PostgrestException catch (e) { + _setLoading(false); + print('Error setting sleep schedule: ${e.message}'); + return e.message; + } catch (e) { + _setLoading(false); + print('Generic error setting sleep schedule: $e'); + return 'An unexpected error occurred.'; + } + } +} diff --git a/newata2/lib/providers/auth_provider.dart b/newata2/lib/providers/auth_provider.dart deleted file mode 100644 index dc30443..0000000 --- a/newata2/lib/providers/auth_provider.dart +++ /dev/null @@ -1,106 +0,0 @@ -// providers/auth_provider.dart (Sudah Lengkap) -import 'package:flutter/foundation.dart'; -import 'package:supabase_flutter/supabase_flutter.dart'; - -class AuthProvider extends ChangeNotifier { - final SupabaseClient _supabase = Supabase.instance.client; - - bool _isLoading = false; - bool get isLoading => _isLoading; - - String? _errorMessage; - String? get errorMessage => _errorMessage; - - User? _currentUser; - User? get currentUser => _currentUser; - - AuthProvider() { - print("AuthProvider: Initializing..."); - _currentUser = _supabase.auth.currentUser; - print("AuthProvider: Initial user state: ${_currentUser?.id ?? 'null'}"); - - _supabase.auth.onAuthStateChange.listen((data) { - final AuthChangeEvent event = data.event; - final Session? session = data.session; - print( - "AuthProvider: onAuthStateChange event: $event, session: ${session?.user.id ?? 'null'}"); - _currentUser = session?.user; - _errorMessage = null; - _isLoading = false; - notifyListeners(); - }); - print("AuthProvider: Listener attached."); - } - - void _setState({bool? loading, String? error}) { - bool changed = false; - final prevLoading = _isLoading; - final prevError = _errorMessage; - - if (loading != null && _isLoading != loading) { - _isLoading = loading; - changed = true; - } - if (_errorMessage != error) { - _errorMessage = error; - changed = true; - } - if (changed) { - print( - "AuthProvider: State changed - isLoading: $prevLoading -> $_isLoading, error: '$prevError' -> '$_errorMessage'"); - notifyListeners(); - } - } - - Future signUp(String email, String password) async { - print("AuthProvider: signUp called with email: $email"); - _setState(loading: true, error: null); - try { - final AuthResponse res = - await _supabase.auth.signUp(email: email, password: password); - print( - "AuthProvider: signUp successful for ${res.user?.email}. User: ${res.user?.id}, Session: ${res.session?.accessToken ?? 'null'}"); - _setState(loading: false, error: null); - return true; - } on AuthException catch (e) { - print('AuthProvider: signUp AuthException: ${e.message}'); - _setState(loading: false, error: 'Registrasi gagal: ${e.message}'); - return false; - } catch (e) { - print('AuthProvider: signUp Unknown Error: $e'); - _setState(loading: false, error: 'Terjadi kesalahan tidak diketahui.'); - return false; - } - } - - Future signIn(String email, String password) async { - print("AuthProvider: signIn called with email: $email"); - _setState(loading: true, error: null); - try { - final AuthResponse res = await _supabase.auth - .signInWithPassword(email: email, password: password); - print( - "AuthProvider: signIn successful for ${res.user?.email}. User: ${res.user?.id}, Session: ${res.session?.accessToken ?? 'null'}"); - _setState(loading: false, error: null); - } on AuthException catch (e) { - print('AuthProvider: signIn AuthException: ${e.message}'); - _setState(loading: false, error: 'Login gagal: ${e.message}'); - } catch (e) { - print('AuthProvider: signIn Unknown Error: $e'); - _setState(loading: false, error: 'Terjadi kesalahan tidak diketahui.'); - } - } - - Future signOut() async { - print("AuthProvider: signOut called for user: ${_currentUser?.id}"); - _setState(loading: true, error: null); - try { - await _supabase.auth.signOut(); - print("AuthProvider: signOut successful."); - _setState(loading: false, error: null); - } catch (e) { - print('AuthProvider: signOut Error: $e'); - _setState(loading: false, error: 'Logout gagal: ${e.toString()}'); - } - } -} diff --git a/newata2/lib/providers/pantau_footage_provider.dart b/newata2/lib/providers/pantau_footage_provider.dart deleted file mode 100644 index 7e37f08..0000000 --- a/newata2/lib/providers/pantau_footage_provider.dart +++ /dev/null @@ -1,71 +0,0 @@ -// providers/footage_provider.dart (Sudah Lengkap) -import 'package:flutter/foundation.dart'; - -import '../constant.dart'; -import '../main.dart'; - -class FootageProvider extends ChangeNotifier { - final String? userId; - FootageProvider(this.userId) { - print("FootageProvider: Initialized with userId: $userId"); - } - - List> _mediaItems = []; - List> get mediaItems => _mediaItems; - - bool _isLoading = false; - bool get isLoading => _isLoading; - - String? _errorMessage; - String? get errorMessage => _errorMessage; - - Future fetchMediaItems() async { - print("FootageProvider: fetchMediaItems called for userId: $userId"); - if (userId == null) { - print("FootageProvider: fetchMediaItems aborted, userId is null."); - _errorMessage = "User tidak terautentikasi."; - notifyListeners(); - return; - } - - _isLoading = true; - _errorMessage = null; - notifyListeners(); - - try { - final response = await supabase - .from('videos') - .select( - 'id, user_id, storage_object_path, uploaded_at, media_type, metadata') - .eq('user_id', userId!) - .order('uploaded_at', ascending: false); - - _mediaItems = List>.from(response as List); - print( - "FootageProvider: fetchMediaItems successful, found ${_mediaItems.length} items."); - } catch (e) { - print('FootageProvider: Error fetching media items: $e'); - _errorMessage = 'Gagal memuat daftar media: ${e.toString()}'; - _mediaItems = []; - } finally { - _isLoading = false; - notifyListeners(); - } - } - - Future getMediaUrl(String storagePath) async { - print("FootageProvider: getMediaUrl called for path: $storagePath"); - try { - final signedUrlResponse = await supabase.storage - .from(AppConstants.footageBucket) - .createSignedUrl(storagePath, 60 * 5); // Expire dalam 5 menit - print("FootageProvider: Signed URL generated successfully."); - return signedUrlResponse; - } catch (e) { - print('FootageProvider: Error getting media URL: $e'); - _errorMessage = 'Gagal mendapatkan URL media.'; - notifyListeners(); - return null; - } - } -} diff --git a/newata2/lib/providers/schedule_provider.dart b/newata2/lib/providers/schedule_provider.dart deleted file mode 100644 index 3b707d5..0000000 --- a/newata2/lib/providers/schedule_provider.dart +++ /dev/null @@ -1,120 +0,0 @@ -// providers/schedule_provider.dart (Sudah Lengkap) -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -import '../main.dart'; - -class ScheduleProvider extends ChangeNotifier { - final String? userId; - ScheduleProvider(this.userId) { - print("ScheduleProvider: Initialized with userId: $userId"); - } - - TimeOfDay? _startTime; - TimeOfDay? get startTime => _startTime; - - TimeOfDay? _endTime; - TimeOfDay? get endTime => _endTime; - - bool _isLoading = false; - bool get isLoading => _isLoading; - - String? _errorMessage; - String? get errorMessage => _errorMessage; - - final _dbTimeFormat = DateFormat('HH:mm:ss'); - - Future fetchSchedule() async { - print("ScheduleProvider: fetchSchedule called for userId: $userId"); - if (userId == null) { - print("ScheduleProvider: fetchSchedule aborted, userId is null."); - _errorMessage = "User tidak terautentikasi."; - notifyListeners(); - return; - } - _isLoading = true; - _errorMessage = null; - notifyListeners(); - - try { - final response = await supabase - .from('device_schedule') - .select('inactive_start_time, inactive_end_time') - .eq('user_id', userId!) - .maybeSingle(); - - if (response != null) { - final data = response; - final startTimeString = data['inactive_start_time'] as String?; - final endTimeString = data['inactive_end_time'] as String?; - - _startTime = (startTimeString != null) - ? TimeOfDay.fromDateTime(_dbTimeFormat.parse(startTimeString)) - : null; - _endTime = (endTimeString != null) - ? TimeOfDay.fromDateTime(_dbTimeFormat.parse(endTimeString)) - : null; - print( - "ScheduleProvider: fetchSchedule successful. Start: $_startTime, End: $_endTime"); - } else { - _startTime = null; - _endTime = null; - print("ScheduleProvider: fetchSchedule - No schedule found."); - } - } catch (e) { - print('ScheduleProvider: Error fetching schedule: $e'); - _errorMessage = 'Gagal memuat jadwal: ${e.toString()}'; - _startTime = null; - _endTime = null; - } finally { - _isLoading = false; - notifyListeners(); - } - } - - Future saveSchedule( - TimeOfDay newStartTime, TimeOfDay newEndTime) async { - print( - "ScheduleProvider: saveSchedule called. Start: $newStartTime, End: $newEndTime"); - if (userId == null) { - print("ScheduleProvider: saveSchedule aborted, userId is null."); - _errorMessage = "User tidak terautentikasi."; - notifyListeners(); - return false; - } - _isLoading = true; - _errorMessage = null; - notifyListeners(); - - try { - final now = DateTime.now(); - final startDateTime = DateTime( - now.year, now.month, now.day, newStartTime.hour, newStartTime.minute); - final endDateTime = DateTime( - now.year, now.month, now.day, newEndTime.hour, newEndTime.minute); - final startTimeString = _dbTimeFormat.format(startDateTime); - final endTimeString = _dbTimeFormat.format(endDateTime); - - await supabase.from('device_schedule').upsert({ - 'user_id': userId!, - 'inactive_start_time': startTimeString, - 'inactive_end_time': endTimeString, - 'updated_at': DateTime.now().toIso8601String(), - }, onConflict: 'user_id'); - - _startTime = newStartTime; - _endTime = newEndTime; - _isLoading = false; - print("ScheduleProvider: saveSchedule successful."); - notifyListeners(); - return true; - } catch (e) { - print('ScheduleProvider: Error saving schedule: $e'); - _errorMessage = 'Gagal menyimpan jadwal: ${e.toString()}'; - _isLoading = false; - notifyListeners(); - return false; - } - } -} diff --git a/newata2/lib/register_page.dart b/newata2/lib/register_page.dart new file mode 100644 index 0000000..2e77f93 --- /dev/null +++ b/newata2/lib/register_page.dart @@ -0,0 +1,124 @@ +import 'package:CCTV_App/main.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +class RegisterPage extends StatefulWidget { + const RegisterPage({super.key}); + @override + State createState() => _RegisterPageState(); +} + +class _RegisterPageState extends State { + final _fullNameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _formKey = GlobalKey(); + bool _isLoading = false; + + Future _signUp() async { + if (!_formKey.currentState!.validate()) return; + + setState(() => _isLoading = true); + try { + await supabase.auth.signUp( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + data: {'full_name': _fullNameController.text.trim()}, + ); + if (!mounted) return; + + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Registrasi berhasil! Silakan periksa email untuk verifikasi & login.'), + backgroundColor: Colors.green, + )); + Navigator.pop(context); + } on AuthException catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.message), + backgroundColor: Colors.redAccent, + )); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("Error: ${e.toString()}"), + backgroundColor: Colors.redAccent, + )); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(backgroundColor: Colors.transparent, elevation: 0), + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(CupertinoIcons.person_add_solid, + size: 80, color: Colors.tealAccent), + const SizedBox(height: 16), + const Text('Buat Akun Baru', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), + const SizedBox(height: 48), + TextFormField( + controller: _fullNameController, + decoration: const InputDecoration(labelText: 'Nama Lengkap'), + validator: (value) => + value!.isEmpty ? 'Nama tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _emailController, + decoration: const InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (value) => + value!.isEmpty ? 'Email tidak boleh kosong' : null, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) => + value!.length < 6 ? 'Password minimal 6 karakter' : null, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _signUp, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.tealAccent, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.black)) + : const Text('Register', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/newata2/lib/screens/control_screen.dart b/newata2/lib/screens/control_screen.dart deleted file mode 100644 index 125b136..0000000 --- a/newata2/lib/screens/control_screen.dart +++ /dev/null @@ -1,722 +0,0 @@ -// screens/control_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../providers/schedule_provider.dart'; // Pastikan path benar - -class ControlScreen extends StatefulWidget { - const ControlScreen({super.key}); - - @override - _ControlScreenState createState() => _ControlScreenState(); -} - -class _ControlScreenState extends State { - // Hapus TextEditingController dan FormKey - // final _startTimeController = TextEditingController(); - // final _endTimeController = TextEditingController(); - // final _formKey = GlobalKey(); - - // Gunakan state lokal untuk menyimpan waktu yang DIPILIH user (sebelum disimpan) - TimeOfDay? _selectedStartTime; - TimeOfDay? _selectedEndTime; - - // State lokal untuk menyimpan waktu dari provider (untuk tampilan jadwal saat ini) - TimeOfDay? _fetchedStartTime; - TimeOfDay? _fetchedEndTime; - - @override - void initState() { - super.initState(); - print("ControlScreen: initState called."); - WidgetsBinding.instance.addPostFrameCallback((_) { - print("ControlScreen: Requesting fetchSchedule."); - final provider = context.read(); - provider.fetchSchedule().then((_) { - if (mounted) { - print( - "ControlScreen: fetchSchedule completed. Updating local state."); - // Set state lokal dari provider saat pertama kali load - setState(() { - _fetchedStartTime = provider.startTime; - _fetchedEndTime = provider.endTime; - // Juga set waktu terpilih awal agar tombol menampilkan nilai yang ada - _selectedStartTime = provider.startTime; - _selectedEndTime = provider.endTime; - }); - } - }); - }); - } - - // Hapus dispose untuk controller - // @override - // void dispose() { - // _startTimeController.dispose(); - // _endTimeController.dispose(); - // super.dispose(); - // } - - // Fungsi untuk menampilkan Time Picker - Future _selectTime(BuildContext context, bool isStartTime) async { - print( - "ControlScreen: Opening time picker for ${isStartTime ? 'start' : 'end'} time."); - // Tentukan waktu awal untuk picker - final TimeOfDay initialTime = isStartTime - ? (_selectedStartTime ?? _fetchedStartTime ?? TimeOfDay.now()) - : (_selectedEndTime ?? - _fetchedEndTime ?? - TimeOfDay( - hour: (TimeOfDay.now().hour + 1) % 24, - minute: TimeOfDay.now().minute)); // Default end time +1 jam - - final TimeOfDay? picked = await showTimePicker( - context: context, - initialTime: initialTime, - helpText: isStartTime ? 'PILIH WAKTU MULAI' : 'PILIH WAKTU SELESAI', - // Gunakan tema dari MaterialApp - // builder: (context, child) { - // return Theme( - // data: Theme.of(context), // Menggunakan tema utama - // child: child!, - // ); - // }, - ); - print("ControlScreen: Time picker closed. Picked: $picked"); - - if (picked != null) { - setState(() { - if (isStartTime) { - _selectedStartTime = picked; - } else { - _selectedEndTime = picked; - } - }); - } - } - - // Format TimeOfDay ke string HH:mm (24 jam) - String _formatTimeOfDay(TimeOfDay? time) { - if (time == null) return 'Belum diatur'; - final hour = time.hour.toString().padLeft(2, '0'); - final minute = time.minute.toString().padLeft(2, '0'); - return '$hour:$minute'; - } - - // Fungsi untuk menyimpan jadwal - Future _saveNewSchedule() async { - print("ControlScreen: Save button pressed."); - // Validasi sederhana: pastikan kedua waktu sudah dipilih - if (_selectedStartTime == null || _selectedEndTime == null) { - print("ControlScreen: Save aborted, start or end time not selected."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Harap pilih waktu mulai dan selesai.'), - backgroundColor: Colors.orangeAccent), - ); - return; - } - - // Validasi tambahan (opsional): cek apakah waktu mulai sebelum waktu selesai - final startTimeMinutes = - _selectedStartTime!.hour * 60 + _selectedStartTime!.minute; - final endTimeMinutes = - _selectedEndTime!.hour * 60 + _selectedEndTime!.minute; - if (startTimeMinutes >= endTimeMinutes) { - print("ControlScreen: Save aborted, start time is not before end time."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Waktu mulai harus sebelum waktu selesai.'), - backgroundColor: Colors.orangeAccent), - ); - return; - } - - final provider = context.read(); - final success = - await provider.saveSchedule(_selectedStartTime!, _selectedEndTime!); - print("ControlScreen: saveSchedule result: $success"); - - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(success - ? 'Jadwal berhasil disimpan!' - : provider.errorMessage ?? 'Gagal menyimpan jadwal.'), - backgroundColor: success ? Colors.green[600] : Colors.redAccent, - behavior: SnackBarBehavior.floating, - ), - ); - if (success) { - // Update state fetched time setelah berhasil simpan - setState(() { - _fetchedStartTime = _selectedStartTime; - _fetchedEndTime = _selectedEndTime; - }); - } - } - } - - @override - Widget build(BuildContext context) { - print("ControlScreen: Building UI."); - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Consumer( - builder: (context, provider, child) { - // Sinkronisasi state lokal jika provider berubah (misal setelah fetch awal) - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && - (_fetchedStartTime != provider.startTime || - _fetchedEndTime != provider.endTime)) { - print( - "ControlScreen Consumer: Provider state changed (fetch), updating local state."); - setState(() { - _fetchedStartTime = provider.startTime; - _fetchedEndTime = provider.endTime; - // Set juga selected time jika belum pernah dipilih user - _selectedStartTime ??= provider.startTime; - _selectedEndTime ??= provider.endTime; - }); - } - }); - - print( - "ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}"); - - // Logika tampilan jadwal saat ini (sama seperti sebelumnya) - String currentScheduleDisplay; - bool isScheduleSet = - _fetchedStartTime != null && _fetchedEndTime != null; - bool hasSchedulePassedToday = false; - if (isScheduleSet) { - final now = TimeOfDay.now(); - final nowInMinutes = now.hour * 60 + now.minute; - final endTimeInMinutes = - _fetchedEndTime!.hour * 60 + _fetchedEndTime!.minute; - if (nowInMinutes > endTimeInMinutes) { - hasSchedulePassedToday = true; - } - } - if (!isScheduleSet || (isScheduleSet && hasSchedulePassedToday)) { - currentScheduleDisplay = - "Belum diatur atau jadwal hari ini telah lewat."; - } else { - currentScheduleDisplay = - '${_formatTimeOfDay(_fetchedStartTime)} - ${_formatTimeOfDay(_fetchedEndTime)}'; - } - - return RefreshIndicator( - onRefresh: () async { - print("ControlScreen: Refresh requested."); - await provider.fetchSchedule(); - // Update state lokal setelah refresh - setState(() { - _fetchedStartTime = provider.startTime; - _fetchedEndTime = provider.endTime; - _selectedStartTime = - provider.startTime; // Reset pilihan user ke data terbaru - _selectedEndTime = provider.endTime; - }); - }, - color: Colors.teal[600]!, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - // Hapus Form widget - // child: Form( - // key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - 'Jadwal Nonaktif CCTV', - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith( - fontWeight: FontWeight.bold, - color: Colors.teal[800]), - ), - const SizedBox(height: 8), - Text( - 'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(color: Colors.grey[700]), - ), - const SizedBox(height: 25), - - // Tampilkan jadwal saat ini - Card( - /* ... (UI Card sama, menampilkan currentScheduleDisplay) ... */ - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Row(/* ... (Header Card sama) ... */), - const SizedBox(height: 10), - if (provider.isLoading && !isScheduleSet) - const Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: Center( - child: CircularProgressIndicator( - strokeWidth: 3, color: Colors.teal)), - ) - else if (provider.errorMessage != null && - !isScheduleSet) - Text(provider.errorMessage!, - style: - const TextStyle(color: Colors.redAccent)) - else - Text( - currentScheduleDisplay, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: (isScheduleSet && - !hasSchedulePassedToday) - ? Colors.black87 - : Colors.grey[600]), - ), - ], - ), - ), - ), - const SizedBox(height: 25), - - // Tombol untuk memilih waktu (kembali ke OutlinedButton) - Text( - 'Setel Jadwal Baru:', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, color: Colors.teal[700]), - ), - const SizedBox(height: 12), - Row( - children: [ - Expanded( - child: OutlinedButton.icon( - icon: const Icon(Icons.timer_outlined, size: 18), - label: Text(_selectedStartTime != null - ? _formatTimeOfDay(_selectedStartTime) - : 'Pilih Mulai'), - onPressed: () => _selectTime( - context, true), // Panggil _selectTime - style: OutlinedButton.styleFrom( - foregroundColor: Colors.teal, - side: BorderSide(color: Colors.teal[200]!), - padding: - const EdgeInsets.symmetric(vertical: 12)), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text("-", - style: TextStyle( - fontSize: 24, color: Colors.grey[600])), - ), - Expanded( - child: OutlinedButton.icon( - icon: - const Icon(Icons.timer_off_outlined, size: 18), - label: Text(_selectedEndTime != null - ? _formatTimeOfDay(_selectedEndTime) - : 'Pilih Selesai'), - onPressed: () => _selectTime( - context, false), // Panggil _selectTime - style: OutlinedButton.styleFrom( - foregroundColor: Colors.teal, - side: BorderSide(color: Colors.teal[200]!), - padding: - const EdgeInsets.symmetric(vertical: 12)), - ), - ), - ], - ), - const SizedBox(height: 30), - - // Tombol Simpan - Center( - child: provider.isLoading - ? CircularProgressIndicator(color: Colors.teal[600]) - : ElevatedButton.icon( - icon: - const Icon(Icons.save_alt_outlined, size: 20), - label: const Text('Simpan Jadwal'), - onPressed: (_selectedStartTime == null || - _selectedEndTime == null) - ? null // Disable jika waktu belum dipilih - : _saveNewSchedule, // Panggil fungsi simpan - ), - ), - // Tampilkan error saving jika ada - if (provider.errorMessage != null && - !provider.isLoading && - (_selectedStartTime != null || - _selectedEndTime != - null)) // Tampilkan jika ada error DAN user sudah mencoba memilih - Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Center( - child: Text(provider.errorMessage!, - style: const TextStyle(color: Colors.redAccent), - textAlign: TextAlign.center), - ), - ), - const SizedBox(height: 20), - ], - ), - // ), // Tutup Form (dihapus) - ), - ); - }, - ), - ), - ); - } -} - -// class ControlScreen extends StatefulWidget { -// const ControlScreen({super.key}); - -// @override -// _ControlScreenState createState() => _ControlScreenState(); -// } - -// class _ControlScreenState extends State { -// TimeOfDay? _selectedStartTime; -// TimeOfDay? _selectedEndTime; - -// @override -// void initState() { -// super.initState(); -// print("ControlScreen: initState called."); -// WidgetsBinding.instance.addPostFrameCallback((_) { -// print("ControlScreen: Requesting fetchSchedule."); -// final provider = context.read(); -// provider.fetchSchedule().then((_) { -// if (mounted) { -// print( -// "ControlScreen: fetchSchedule completed. Updating local state."); -// setState(() { -// _selectedStartTime = provider.startTime; -// _selectedEndTime = provider.endTime; -// }); -// } -// }); -// }); -// } - -// Future _selectTime( -// BuildContext context, TimeOfDay? initialTime) async { -// print("ControlScreen: Opening time picker."); -// final TimeOfDay? picked = await showTimePicker( -// context: context, -// initialTime: initialTime ?? TimeOfDay.now(), -// helpText: 'PILIH WAKTU', // Ubah teks helper -// builder: (context, child) { -// // Gunakan tema dari MaterialApp -// return child!; -// // Atau override tema spesifik di sini jika perlu -// // return Theme( -// // data: Theme.of(context).copyWith( -// // timePickerTheme: TimePickerThemeData(...) // Override spesifik -// // ), -// // child: child!, -// // ); -// }, -// ); -// print("ControlScreen: Time picker closed. Picked: $picked"); -// return picked; -// } - -// String _formatTimeOfDay(TimeOfDay? time) { -// if (time == null) return 'Belum diatur'; -// final now = DateTime.now(); -// final dt = DateTime(now.year, now.month, now.day, time.hour, time.minute); -// return DateFormat('HH:mm', 'id_ID') -// .format(dt); // Gunakan format 24 jam agar lebih jelas -// } - -// @override -// Widget build(BuildContext context) { -// print("ControlScreen: Building UI."); -// return Scaffold( -// // AppBar tidak perlu karena sudah ada di Dashboard -// body: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Consumer( -// builder: (context, provider, child) { -// // Sinkronisasi state lokal -// WidgetsBinding.instance.addPostFrameCallback((_) { -// if (mounted && -// (_selectedStartTime != provider.startTime || -// _selectedEndTime != provider.endTime)) { -// print( -// "ControlScreen Consumer: Provider state changed, updating local state."); -// setState(() { -// _selectedStartTime = provider.startTime; -// _selectedEndTime = provider.endTime; -// }); -// } -// }); - -// print( -// "ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}"); - -// return RefreshIndicator( -// // Tambahkan RefreshIndicator -// onRefresh: () async { -// print("ControlScreen: Refresh requested."); -// await provider.fetchSchedule(); -// }, -// color: Colors.teal, -// child: SingleChildScrollView( -// physics: -// const AlwaysScrollableScrollPhysics(), // Agar bisa refresh walau konten pendek -// child: Column( -// crossAxisAlignment: -// CrossAxisAlignment.stretch, // Buat elemen memenuhi lebar -// children: [ -// Text( -// 'Jadwal Nonaktif CCTV', -// style: Theme.of(context) -// .textTheme -// .headlineSmall -// ?.copyWith( -// fontWeight: FontWeight.bold, -// color: Colors.teal[800]), -// ), -// const SizedBox(height: 8), -// Text( -// 'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.', -// style: Theme.of(context) -// .textTheme -// .bodyMedium -// ?.copyWith(color: Colors.grey[700]), -// ), -// const SizedBox(height: 25), - -// // Tampilkan jadwal saat ini -// Card( -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Row( -// // Tambahkan ikon dan tombol refresh -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: [ -// Text( -// 'Jadwal Saat Ini:', -// style: Theme.of(context) -// .textTheme -// .titleMedium -// ?.copyWith( -// fontWeight: FontWeight.w600, -// color: Colors.teal[700]), -// ), -// // Tombol refresh kecil -// IconButton( -// icon: Icon(Icons.refresh, -// color: Colors.grey[500]), -// tooltip: 'Muat Ulang Jadwal', -// iconSize: 20, -// onPressed: provider.isLoading -// ? null -// : () async { -// print( -// "ControlScreen: Inline refresh pressed."); -// await provider.fetchSchedule(); -// }, -// ) -// ], -// ), -// const SizedBox(height: 10), -// if (provider.isLoading && -// _selectedStartTime == null) -// const Padding( -// padding: EdgeInsets.symmetric(vertical: 10.0), -// child: Center( -// child: CircularProgressIndicator( -// strokeWidth: 3, color: Colors.teal)), -// ) -// else if (provider.errorMessage != null && -// _selectedStartTime == null) -// Text(provider.errorMessage!, -// style: -// const TextStyle(color: Colors.redAccent)) -// else -// Row( -// mainAxisAlignment: -// MainAxisAlignment.spaceAround, // Beri jarak -// children: [ -// _buildTimeDisplay( -// 'Mulai Nonaktif', _selectedStartTime), -// Icon(Icons.arrow_forward, -// color: Colors -// .grey[400]), // Panah penanda rentang -// _buildTimeDisplay( -// 'Selesai Nonaktif', _selectedEndTime), -// ], -// ), -// ], -// ), -// ), -// ), -// const SizedBox(height: 25), - -// // Tombol untuk memilih waktu -// Text( -// 'Setel Jadwal Baru:', -// style: Theme.of(context).textTheme.titleMedium?.copyWith( -// fontWeight: FontWeight.w600, color: Colors.teal[700]), -// ), -// const SizedBox(height: 10), -// Row( -// children: [ -// Expanded( -// child: OutlinedButton.icon( -// icon: const Icon(Icons.timer_outlined, -// size: 18), // Kecilkan ikon -// label: Text(_selectedStartTime != null -// ? _formatTimeOfDay(_selectedStartTime) -// : 'Pilih Mulai'), -// onPressed: () async { -// final time = await _selectTime( -// context, _selectedStartTime); -// if (time != null) { -// setState(() { -// _selectedStartTime = time; -// }); -// } -// }, -// style: OutlinedButton.styleFrom( -// foregroundColor: Colors.teal, -// side: BorderSide(color: Colors.teal[200]!), -// padding: const EdgeInsets.symmetric( -// vertical: 12) // Sesuaikan padding -// ), -// ), -// ), -// const SizedBox(width: 10), -// Expanded( -// child: OutlinedButton.icon( -// icon: const Icon(Icons.timer_off_outlined, -// size: 18), // Kecilkan ikon -// label: Text(_selectedEndTime != null -// ? _formatTimeOfDay(_selectedEndTime) -// : 'Pilih Selesai'), -// onPressed: () async { -// final time = -// await _selectTime(context, _selectedEndTime); -// if (time != null) { -// setState(() { -// _selectedEndTime = time; -// }); -// } -// }, -// style: OutlinedButton.styleFrom( -// foregroundColor: Colors.teal, -// side: BorderSide(color: Colors.teal[200]!), -// padding: const EdgeInsets.symmetric( -// vertical: 12) // Sesuaikan padding -// ), -// ), -// ), -// ], -// ), -// const SizedBox( -// height: 30), // Beri jarak lebih sebelum tombol simpan - -// // Tombol Simpan -// Center( -// child: provider.isLoading -// ? const CircularProgressIndicator(color: Colors.teal) -// : ElevatedButton.icon( -// icon: -// const Icon(Icons.save_alt_outlined, size: 20), -// label: const Text('Simpan Jadwal', -// style: TextStyle(fontSize: 16)), -// style: ElevatedButton.styleFrom( -// padding: const EdgeInsets.symmetric( -// vertical: 14, -// horizontal: -// 30)), // Buat tombol lebih besar -// onPressed: (_selectedStartTime == null || -// _selectedEndTime == null) -// ? null // Disable jika waktu belum dipilih -// : () async { -// print( -// "ControlScreen: Save button pressed."); -// final success = await context -// .read() -// .saveSchedule(_selectedStartTime!, -// _selectedEndTime!); -// print( -// "ControlScreen: saveSchedule result: $success"); - -// if (mounted) { -// ScaffoldMessenger.of(context) -// .showSnackBar( -// SnackBar( -// content: Text(success -// ? 'Jadwal berhasil disimpan!' -// : context -// .read< -// ScheduleProvider>() -// .errorMessage ?? -// 'Gagal menyimpan jadwal.'), -// backgroundColor: success -// ? Colors.green[600] -// : Colors.redAccent, -// behavior: SnackBarBehavior -// .floating, // SnackBar mengambang -// ), -// ); -// } -// }, -// ), -// ), -// // Tampilkan error saving jika ada -// if (provider.errorMessage != null && - // !provider.isLoading && -// _selectedStartTime != -// null) // Tampilkan hanya jika user mencoba menyimpan -// Padding( -// padding: const EdgeInsets.only(top: 15.0), -// child: Center( -// child: Text(provider.errorMessage!, -// style: const TextStyle(color: Colors.redAccent), -// textAlign: TextAlign.center), -// ), -// ), -// const SizedBox(height: 20), // Padding bawah -// ], -// ), -// ), -// ); -// }, -// ), -// ), -// ); -// } - -// // Helper widget untuk menampilkan waktu -// Widget _buildTimeDisplay(String label, TimeOfDay? time) { -// return Column( -// crossAxisAlignment: CrossAxisAlignment.center, -// children: [ -// Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 12)), -// const SizedBox(height: 2), -// Text( -// _formatTimeOfDay(time), -// style: const TextStyle( -// fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87), -// ), -// ], -// ); -// } -// } diff --git a/newata2/lib/screens/dashboard_screen.dart b/newata2/lib/screens/dashboard_screen.dart deleted file mode 100644 index 9305596..0000000 --- a/newata2/lib/screens/dashboard_screen.dart +++ /dev/null @@ -1,82 +0,0 @@ -// screens/dashboard_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../providers/auth_provider.dart'; // Pastikan path benar -import 'footage_screen.dart'; // Pastikan path benar -import 'control_screen.dart'; // Pastikan path benar - -class DashboardScreen extends StatefulWidget { - const DashboardScreen({super.key}); - - @override - _DashboardScreenState createState() => _DashboardScreenState(); -} - -class _DashboardScreenState extends State { - int _selectedIndex = 0; - - static final List _widgetOptions = [ - const FootageScreen(), - const ControlScreen(), - ]; - - void _onItemTapped(int index) { - print("DashboardScreen: Tab tapped, index: $index"); - setState(() { - _selectedIndex = index; - }); - } - - @override - Widget build(BuildContext context) { - print("DashboardScreen: Building UI."); - // Gunakan watch agar UI rebuild jika user berubah (meski tidak mungkin di screen ini) - final userIdentifier = - context.watch().currentUser?.email ?? 'Pengguna'; - print("DashboardScreen: Current user identifier: $userIdentifier"); - - return Scaffold( - appBar: AppBar( - title: Text('Halo, $userIdentifier!'), - actions: [ - IconButton( - icon: const Icon(Icons.logout), - tooltip: 'Keluar', - onPressed: () async { - print("DashboardScreen: Logout button pressed."); - await context.read().signOut(); - if (mounted && context.read().currentUser == null) { - print( - "DashboardScreen: User is null after sign out, navigating to /login"); - Navigator.pushNamedAndRemoveUntil( - context, '/login', (route) => false); - } - }, - ), - ], - ), - body: IndexedStack( - // Pertahankan state tiap halaman saat ganti tab - index: _selectedIndex, - children: _widgetOptions, - ), - bottomNavigationBar: BottomNavigationBar( - type: BottomNavigationBarType.fixed, // Tipe agar label selalu terlihat - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.video_library_outlined), - activeIcon: Icon(Icons.video_library), - label: 'Footage', - ), - BottomNavigationBarItem( - icon: Icon(Icons.settings_remote_outlined), - activeIcon: Icon(Icons.settings_remote), - label: 'Kontrol Alat', - ), - ], - currentIndex: _selectedIndex, - onTap: _onItemTapped, - ), - ); - } -} diff --git a/newata2/lib/screens/footage_screen.dart b/newata2/lib/screens/footage_screen.dart deleted file mode 100644 index fccf24b..0000000 --- a/newata2/lib/screens/footage_screen.dart +++ /dev/null @@ -1,404 +0,0 @@ -// screens/footage_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:intl/intl.dart'; -import '../providers/pantau_footage_provider.dart'; // Pastikan path benar - -// class FootageScreen extends StatefulWidget { -// const FootageScreen({super.key}); - -// @override -// _FootageScreenState createState() => _FootageScreenState(); -// } - -// class _FootageScreenState extends State { -// @override -// void initState() { -// super.initState(); -// print("FootageScreen: initState called."); -// WidgetsBinding.instance.addPostFrameCallback((_) { -// print("FootageScreen: Requesting fetchMediaItems."); -// context.read().fetchMediaItems(); -// }); -// } - -// Future _refreshMediaItems() async { -// print("FootageScreen: Refresh requested."); -// await context.read().fetchMediaItems(); -// } - -// void _viewMedia(BuildContext context, Map mediaItem) async { -// final storagePath = mediaItem['storage_object_path'] as String?; -// final mediaType = mediaItem['media_type'] as String?; - -// if (storagePath == null) { -// print("FootageScreen: View media failed, storage path is null."); -// ScaffoldMessenger.of(context).showSnackBar( -// const SnackBar( -// content: Text('Path media tidak valid.'), -// backgroundColor: Colors.orangeAccent), -// ); -// return; -// } -// print( -// "FootageScreen: Viewing media type '$mediaType' at path '$storagePath'"); - -// showDialog( -// context: context, -// barrierDismissible: false, -// builder: (BuildContext context) => const Dialog( -// child: Padding( -// padding: EdgeInsets.all(20.0), -// child: Row(mainAxisSize: MainAxisSize.min, children: [ -// CircularProgressIndicator(color: Colors.teal), -// SizedBox(width: 20), -// Text("Memuat media...") -// ])), -// ), -// ); - -// final url = await context.read().getMediaUrl(storagePath); -// if (mounted) Navigator.pop(context); // Tutup dialog loading - -// if (url != null && mounted) { -// print("FootageScreen: URL obtained: $url"); -// if (mediaType == 'video') { -// print("FootageScreen: Navigating to /video_player"); -// Navigator.pushNamed(context, '/video_player', arguments: url); -// } else if (mediaType == 'image') { -// print("FootageScreen: Navigating to /image_viewer"); -// Navigator.pushNamed(context, '/image_viewer', arguments: url); -// } else { -// print("FootageScreen: Unknown media type '$mediaType', cannot open."); -// ScaffoldMessenger.of(context).showSnackBar( -// SnackBar( -// content: Text('Tipe media tidak dikenali: $mediaType'), -// backgroundColor: Colors.orangeAccent), -// ); -// } -// } else if (mounted) { -// print("FootageScreen: Failed to get URL."); -// ScaffoldMessenger.of(context).showSnackBar( -// SnackBar( -// content: Text(context.read().errorMessage ?? -// 'Gagal memuat URL media.'), -// backgroundColor: Colors.redAccent), -// ); -// } -// } - -// @override -// Widget build(BuildContext context) { -// print("FootageScreen: Building UI."); -// final dateTimeFormat = -// DateFormat('EEEE, dd MMMM yyyy HH:mm', 'id_ID'); // Tambah tahun - -// return Scaffold( -// // AppBar tidak perlu karena sudah ada di Dashboard -// body: Consumer( -// builder: (context, provider, child) { -// print( -// "FootageScreen Consumer: Building list. isLoading: ${provider.isLoading}, itemCount: ${provider.mediaItems.length}, error: ${provider.errorMessage}"); - -// if (provider.isLoading && provider.mediaItems.isEmpty) { -// return const Center( -// child: CircularProgressIndicator(color: Colors.teal)); -// } - -// if (provider.errorMessage != null) { -// return Center( -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// const Icon(Icons.error_outline, -// color: Colors.redAccent, size: 50), -// const SizedBox(height: 10), -// Text('Oops! Terjadi Kesalahan', -// style: Theme.of(context).textTheme.titleLarge, -// textAlign: TextAlign.center), -// const SizedBox(height: 5), -// Text(provider.errorMessage!, -// textAlign: TextAlign.center, -// style: TextStyle(color: Colors.grey[700])), -// const SizedBox(height: 20), -// ElevatedButton.icon( -// icon: const Icon(Icons.refresh), -// label: const Text('Coba Lagi'), -// onPressed: _refreshMediaItems) -// ], -// ), -// ), -// ); -// } - -// if (provider.mediaItems.isEmpty && !provider.isLoading) { -// return Center( -// child: Column( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Icon(Icons.videocam_off_outlined, -// size: 60, color: Colors.grey[400]), -// const SizedBox(height: 10), -// Text('Belum ada rekaman media', -// style: Theme.of(context) -// .textTheme -// .titleMedium -// ?.copyWith(color: Colors.grey[600])), -// const SizedBox(height: 20), -// ElevatedButton.icon( -// icon: const Icon(Icons.refresh), -// label: const Text('Refresh'), -// onPressed: _refreshMediaItems, -// style: ElevatedButton.styleFrom( -// backgroundColor: Colors.grey[300], -// foregroundColor: Colors.grey[800])) -// ], -// )); -// } - -// // Tampilkan list media -// return RefreshIndicator( -// onRefresh: _refreshMediaItems, -// color: Colors.teal, -// child: ListView.builder( -// padding: const EdgeInsets.all(8.0), -// itemCount: provider.mediaItems.length, -// itemBuilder: (context, index) { -// final mediaItem = provider.mediaItems[index]; -// final storagePath = mediaItem['storage_object_path'] as String?; -// final uploadedAtString = mediaItem['uploaded_at'] as String?; -// final mediaType = mediaItem['media_type'] as String?; -// DateTime? uploadedAt = (uploadedAtString != null) -// ? DateTime.tryParse(uploadedAtString)?.toLocal() -// : null; -// final fileName = -// storagePath?.split('/').last ?? 'Media Tidak Dikenal'; - -// IconData leadingIconData = Icons.perm_media_outlined; -// Color iconColor = Colors.teal[700]!; -// if (mediaType == 'video') { -// leadingIconData = Icons.play_circle_outline; -// } else if (mediaType == 'image') { -// leadingIconData = Icons.image_outlined; -// iconColor = Colors.indigo[700]!; -// } - -// return Card( -// clipBehavior: Clip -// .antiAlias, // Agar ripple effect tidak keluar batas card -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: iconColor.withOpacity(0.1), -// child: Icon(leadingIconData, color: iconColor), -// ), -// title: Text( -// fileName, -// style: const TextStyle(fontWeight: FontWeight.w500), -// maxLines: 1, -// overflow: TextOverflow.ellipsis, -// ), -// subtitle: Text( -// uploadedAt != null -// ? dateTimeFormat.format(uploadedAt) -// : 'Tanggal tidak diketahui', -// style: TextStyle(color: Colors.grey[600], fontSize: 12), -// ), -// trailing: -// Icon(Icons.chevron_right, color: Colors.grey[400]), -// onTap: () => _viewMedia(context, mediaItem), -// ), -// ); -// }, -// ), -// ); -// }, -// ), -// ); -// } -// } - -class FootageScreen extends StatefulWidget { - const FootageScreen({super.key}); - - @override - _FootageScreenState createState() => _FootageScreenState(); -} - -class _FootageScreenState extends State { - @override - void initState() { - super.initState(); - print("FootageScreen: initState called."); - WidgetsBinding.instance.addPostFrameCallback((_) { - print("FootageScreen: Requesting fetchMediaItems."); - context.read().fetchMediaItems(); - }); - } - - Future _refreshMediaItems() async { - print("FootageScreen: Refresh requested."); - await context.read().fetchMediaItems(); - } - - void _viewMedia(BuildContext context, Map mediaItem) async { - final storagePath = mediaItem['storage_object_path'] as String?; - final mediaType = mediaItem['media_type'] as String?; - - if (storagePath == null) { - print("FootageScreen: View media failed, storage path is null."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Path media tidak valid.'), - backgroundColor: Colors.orangeAccent), - ); - return; - } - print( - "FootageScreen: Viewing media type '$mediaType' at path '$storagePath'"); - - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) => Dialog( - backgroundColor: Colors.white, // Background dialog - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 24.0, horizontal: 20.0), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - CircularProgressIndicator(color: Colors.teal[600]), - const SizedBox(width: 24), - const Text("Memuat media...", style: TextStyle(fontSize: 16)), - ]), - )), - ); - - final url = await context.read().getMediaUrl(storagePath); - if (mounted) Navigator.pop(context); - - if (url != null && mounted) { - /* ... (logika navigasi sama) ... */ - } else if (mounted) {/* ... (logika error sama) ... */} - } - - @override - Widget build(BuildContext context) { - print("FootageScreen: Building UI."); - // final dateTimeFormat = - // DateFormat('EEEE, dd MMMM yyyy HH:mm', 'id_ID'); // Tambah tahun - - return Scaffold( - body: Consumer( - builder: (context, provider, child) { - print( - "FootageScreen Consumer: Building list. isLoading: ${provider.isLoading}, itemCount: ${provider.mediaItems.length}, error: ${provider.errorMessage}"); - - if (provider.isLoading && provider.mediaItems.isEmpty) { - return Center( - child: CircularProgressIndicator(color: Colors.teal[600])); - } - - if (provider.errorMessage != null) { - return const Center( - /* ... (Widget error sama, mungkin perbesar font) ... */); - } - - if (provider.mediaItems.isEmpty && !provider.isLoading) { - return const Center( - /* ... (Widget 'belum ada rekaman' sama, mungkin perbesar font/ikon) ... */); - } - - // Gunakan GridView untuk tampilan lebih modern dan mengisi ruang - return RefreshIndicator( - onRefresh: _refreshMediaItems, - color: Colors.teal[600]!, - child: GridView.builder( - padding: - const EdgeInsets.all(12.0), // Padding lebih besar untuk grid - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: MediaQuery.of(context).size.width > 600 - ? 3 - : 2, // 2 kolom di HP, 3 di tablet - crossAxisSpacing: 12.0, - mainAxisSpacing: 12.0, - childAspectRatio: - 1.0, // Buat item kotak, atau sesuaikan (misal 3/2 untuk landscape) - ), - itemCount: provider.mediaItems.length, - itemBuilder: (context, index) { - final mediaItem = provider.mediaItems[index]; - final storagePath = mediaItem['storage_object_path'] as String?; - final uploadedAtString = mediaItem['uploaded_at'] as String?; - final mediaType = mediaItem['media_type'] as String?; - DateTime? uploadedAt = (uploadedAtString != null) - ? DateTime.tryParse(uploadedAtString)?.toLocal() - : null; - final fileName = - storagePath?.split('/').last ?? 'Media Tidak Dikenal'; - - IconData itemIconData = Icons.perm_media_outlined; - Color itemIconColor = Colors.teal[700]!; - if (mediaType == 'video') { - itemIconData = Icons.play_circle_filled_outlined; - } else if (mediaType == 'image') { - itemIconData = Icons.image; - itemIconColor = Colors.indigo[700]!; - } - - return Card( - clipBehavior: Clip.antiAlias, - elevation: 3, // Shadow lebih tegas - child: InkWell( - // Tambahkan InkWell untuk ripple effect - onTap: () => _viewMedia(context, mediaItem), - child: GridTile( - footer: Container( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - color: Colors.black.withOpacity(0.6), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - fileName, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - fontSize: 13), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (uploadedAt != null) - Text( - DateFormat('dd MMM yy, HH:mm', 'id_ID') - .format(uploadedAt), - style: TextStyle( - color: Colors.grey[300], fontSize: 11), - ), - ], - ), - ), - child: Container( - // Latar belakang untuk ikon jika tidak ada thumbnail - color: itemIconColor.withOpacity(0.05), - child: Center( - child: Icon(itemIconData, - size: 50, color: itemIconColor.withOpacity(0.7)), - ), - // TODO: Jika Anda punya URL thumbnail, ganti dengan Image.network di sini - // child: mediaItem['thumbnail_url'] != null - // ? Image.network(mediaItem['thumbnail_url'], fit: BoxFit.cover) - // : Center(child: Icon(itemIconData, size: 50, color: itemIconColor.withOpacity(0.7))), - ), - ), - ), - ); - }, - ), - ); - }, - ), - ); - } -} diff --git a/newata2/lib/screens/image_viewer_screen.dart b/newata2/lib/screens/image_viewer_screen.dart deleted file mode 100644 index 16a7e32..0000000 --- a/newata2/lib/screens/image_viewer_screen.dart +++ /dev/null @@ -1,60 +0,0 @@ -// screens/image_viewer_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; - -class ImageViewerScreen extends StatelessWidget { - final String imageUrl; - - const ImageViewerScreen({super.key, required this.imageUrl}); - - @override - Widget build(BuildContext context) { - print("ImageViewerScreen: Building UI with URL: $imageUrl"); - return Scaffold( - backgroundColor: Colors.black, - appBar: AppBar( - title: const Text('Penampil Gambar'), - backgroundColor: Colors.black.withOpacity(0.7), - elevation: 0, - ), - body: Center( - child: InteractiveViewer( - panEnabled: true, - minScale: 0.5, - maxScale: 4.0, - child: Image.network( - imageUrl, - fit: BoxFit.contain, - loadingBuilder: (BuildContext context, Widget child, - ImageChunkEvent? loadingProgress) { - if (loadingProgress == null) return child; - return Center( - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null, - color: Colors.white, - ), - ); - }, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - print("ImageViewerScreen: Error loading image: $exception"); - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.broken_image_outlined, - color: Colors.grey[600], size: 60), - const SizedBox(height: 10), - Text('Gagal memuat gambar', - style: TextStyle(color: Colors.grey[400])), - ], - )); - }, - ), - ), - ), - ); - } -} diff --git a/newata2/lib/screens/login_screen.dart b/newata2/lib/screens/login_screen.dart deleted file mode 100644 index f281412..0000000 --- a/newata2/lib/screens/login_screen.dart +++ /dev/null @@ -1,207 +0,0 @@ -// screens/login_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../providers/auth_provider.dart'; // Pastikan path benar - -class LoginScreen extends StatefulWidget { - const LoginScreen({super.key}); - - @override - _LoginScreenState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends State { - final _formKey = GlobalKey(); - final _emailController = TextEditingController(); - final _passwordController = TextEditingController(); - bool _isPasswordVisible = false; - - @override - void dispose() { - _emailController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - - Future _performLogin() async { - print("LoginScreen: _performLogin called."); - FocusScope.of(context).unfocus(); - - if (_formKey.currentState!.validate()) { - print("LoginScreen: Form is valid."); - final authProvider = context.read(); - await authProvider.signIn( - _emailController.text.trim(), - _passwordController.text.trim(), - ); - } else { - print("LoginScreen: Form is invalid."); - } - } - - @override - Widget build(BuildContext context) { - print("LoginScreen: Building UI."); - return Scaffold( - body: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.all(30.0), - child: Form( - key: _formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.cut_outlined, size: 80, color: Colors.teal[400]), - const SizedBox(height: 20), - Text( - 'Selamat Datang Kembali!', - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, color: Colors.teal[800]), - ), - const SizedBox(height: 8), - Text( - 'Masuk ke akun Anda', - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith(color: Colors.grey[600]), - textAlign: TextAlign.center, - ), - const SizedBox(height: 30), - - // Input Email - TextFormField( - controller: _emailController, - decoration: InputDecoration( - labelText: 'Email', - hintText: 'Masukkan email Anda', // Tambah hint - prefixIcon: - Icon(Icons.email_outlined, color: Colors.grey[600]), - ), - keyboardType: TextInputType.emailAddress, - autovalidateMode: AutovalidateMode - .onUserInteraction, // Validasi saat interaksi - validator: (value) { - if (value == null || value.isEmpty) - return 'Email tidak boleh kosong'; - if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) - return 'Masukkan format email yang valid'; - return null; - }, - ), - const SizedBox(height: 15), - - // Input Password - TextFormField( - controller: _passwordController, - decoration: InputDecoration( - labelText: 'Password', - hintText: 'Masukkan password Anda', // Tambah hint - prefixIcon: - Icon(Icons.lock_outline, color: Colors.grey[600]), - suffixIcon: IconButton( - icon: Icon( - _isPasswordVisible - ? Icons.visibility_off_outlined - : Icons.visibility_outlined, - color: Colors.grey[600], - ), - onPressed: () => setState( - () => _isPasswordVisible = !_isPasswordVisible)), - ), - obscureText: !_isPasswordVisible, - autovalidateMode: AutovalidateMode - .onUserInteraction, // Validasi saat interaksi - validator: (value) { - if (value == null || value.isEmpty) - return 'Password tidak boleh kosong'; - if (value.length < 6) return 'Password minimal 6 karakter'; - return null; - }, - ), - const SizedBox(height: 25), - - // Tombol Login & Loading Indicator - Consumer( - builder: (context, authProvider, child) { - if (authProvider.currentUser != null && - !authProvider.isLoading) { - print( - "LoginScreen Consumer: User detected (${authProvider.currentUser!.id}), navigating to dashboard."); - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && - ModalRoute.of(context)?.isCurrent == true) { - Navigator.pushReplacementNamed(context, '/dashboard'); - } - }); - return const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: CircularProgressIndicator(color: Colors.teal), - ); - } - - return authProvider.isLoading - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: - CircularProgressIndicator(color: Colors.teal), - ) - : SizedBox( - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric( - vertical: - 14)), // Buat tombol lebih tinggi - child: const Text('Login', - style: TextStyle(fontSize: 16)), - onPressed: _performLogin, - ), - ); - }, - ), - const SizedBox(height: 15), - - // Link ke Halaman Register - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("Belum punya akun?", - style: TextStyle(color: Colors.grey[700])), - TextButton( - child: const Text('Daftar di sini'), - onPressed: () { - print("LoginScreen: Navigating to /register"); - Navigator.pushNamed(context, '/register'); - }, - ), - ], - ), - const SizedBox(height: 10), - - // Tampilkan Pesan Error - Consumer( - builder: (context, auth, child) { - if (auth.errorMessage != null && !auth.isLoading) { - print( - "LoginScreen Consumer: Displaying error message: ${auth.errorMessage}"); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - auth.errorMessage!, - style: const TextStyle(color: Colors.redAccent), - textAlign: TextAlign.center, - ), - ); - } - return const SizedBox.shrink(); - }, - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/newata2/lib/screens/register_screen.dart b/newata2/lib/screens/register_screen.dart deleted file mode 100644 index 8d75902..0000000 --- a/newata2/lib/screens/register_screen.dart +++ /dev/null @@ -1,239 +0,0 @@ -// screens/register_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../providers/auth_provider.dart'; // Pastikan path benar - -class RegisterScreen extends StatefulWidget { - const RegisterScreen({super.key}); - - @override - _RegisterScreenState createState() => _RegisterScreenState(); -} - -class _RegisterScreenState extends State { - final _formKey = GlobalKey(); - final _emailController = TextEditingController(); - final _passwordController = TextEditingController(); - final _confirmPasswordController = TextEditingController(); - bool _isPasswordVisible = false; - bool _isConfirmPasswordVisible = false; - String? _successMessage; - - @override - void dispose() { - _emailController.dispose(); - _passwordController.dispose(); - _confirmPasswordController.dispose(); - super.dispose(); - } - - Future _performRegister() async { - print("RegisterScreen: _performRegister called."); - setState(() { - _successMessage = null; - }); - FocusScope.of(context).unfocus(); - - if (_formKey.currentState!.validate()) { - print("RegisterScreen: Form is valid."); - final authProvider = context.read(); - final bool success = await authProvider.signUp( - _emailController.text.trim(), - _passwordController.text.trim(), - ); - - if (mounted && success) { - print("RegisterScreen: SignUp reported success from provider."); - setState(() { - _successMessage = - "Registrasi berhasil! Jika konfirmasi email aktif, silakan cek email Anda sebelum login."; - }); - _formKey.currentState?.reset(); - _emailController.clear(); - _passwordController.clear(); - _confirmPasswordController.clear(); - } else if (mounted) { - print("RegisterScreen: SignUp reported failure from provider."); - } - } else { - print("RegisterScreen: Form is invalid."); - } - } - - @override - Widget build(BuildContext context) { - print("RegisterScreen: Building UI."); - return Scaffold( - appBar: AppBar( - title: const Text('Daftar Akun Baru'), - backgroundColor: Colors.transparent, - elevation: 0, - foregroundColor: Colors.teal[800], - iconTheme: IconThemeData(color: Colors.teal[800]), // Warna ikon back - ), - body: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.all(30.0), - child: Form( - key: _formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.person_add_alt_1_outlined, - size: 60, color: Colors.teal[400]), - const SizedBox(height: 15), - Text( - 'Buat Akun Anda', - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, color: Colors.teal[800]), - ), - const SizedBox(height: 25), - - // Input Email - TextFormField( - controller: _emailController, - decoration: const InputDecoration( - labelText: 'Email', - hintText: 'Masukkan email Anda', - prefixIcon: Icon(Icons.email_outlined)), - keyboardType: TextInputType.emailAddress, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value) { - if (value == null || value.isEmpty) - return 'Email tidak boleh kosong'; - if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) - return 'Masukkan format email yang valid'; - return null; - }, - ), - const SizedBox(height: 15), - - // Input Password - TextFormField( - controller: _passwordController, - decoration: InputDecoration( - labelText: 'Password', - hintText: 'Minimal 6 karakter', - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon(_isPasswordVisible - ? Icons.visibility_off_outlined - : Icons.visibility_outlined), - onPressed: () => setState( - () => _isPasswordVisible = !_isPasswordVisible))), - obscureText: !_isPasswordVisible, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value) { - if (value == null || value.isEmpty) - return 'Password tidak boleh kosong'; - if (value.length < 6) return 'Password minimal 6 karakter'; - return null; - }, - ), - const SizedBox(height: 15), - - // Input Konfirmasi Password - TextFormField( - controller: _confirmPasswordController, - decoration: InputDecoration( - labelText: 'Konfirmasi Password', - hintText: 'Ulangi password', - prefixIcon: const Icon(Icons.lock_clock_outlined), - suffixIcon: IconButton( - icon: Icon(_isConfirmPasswordVisible - ? Icons.visibility_off_outlined - : Icons.visibility_outlined), - onPressed: () => setState(() => - _isConfirmPasswordVisible = - !_isConfirmPasswordVisible))), - obscureText: !_isConfirmPasswordVisible, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value) { - if (value == null || value.isEmpty) - return 'Konfirmasi password tidak boleh kosong'; - if (value != _passwordController.text) - return 'Password tidak cocok'; - return null; - }, - ), - const SizedBox(height: 25), - - // Tampilkan Pesan Sukses jika ada - if (_successMessage != null) - Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Text( - _successMessage!, - style: TextStyle( - color: Colors.green[700], - fontWeight: FontWeight.bold), - textAlign: TextAlign.center, - ), - ), - - // Tombol Daftar & Loading Indicator - Consumer( - builder: (context, authProvider, child) { - if (_successMessage != null) { - // Jika sukses, tampilkan tombol kembali ke login - return SizedBox( - width: double.infinity, - child: OutlinedButton( - onPressed: () => Navigator.pop(context), - style: OutlinedButton.styleFrom( - foregroundColor: Colors.teal, - side: BorderSide(color: Colors.teal[200]!), - padding: - const EdgeInsets.symmetric(vertical: 14)), - child: const Text('Kembali ke Login'), - ), - ); - } - return authProvider.isLoading - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: - CircularProgressIndicator(color: Colors.teal), - ) - : SizedBox( - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - padding: - const EdgeInsets.symmetric(vertical: 14)), - onPressed: _performRegister, - child: const Text('Daftar', - style: TextStyle(fontSize: 16)), - ), - ); - }, - ), - const SizedBox(height: 15), - - // Tampilkan Pesan Error (Hanya jika tidak ada pesan sukses) - if (_successMessage == null) - Consumer( - builder: (context, auth, child) { - if (auth.errorMessage != null && !auth.isLoading) { - print( - "RegisterScreen Consumer: Displaying error message: ${auth.errorMessage}"); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - auth.errorMessage!, - style: const TextStyle(color: Colors.redAccent), - textAlign: TextAlign.center, - ), - ); - } - return const SizedBox.shrink(); - }, - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/newata2/lib/screens/splash_screen.dart b/newata2/lib/screens/splash_screen.dart deleted file mode 100644 index 3d81e18..0000000 --- a/newata2/lib/screens/splash_screen.dart +++ /dev/null @@ -1,57 +0,0 @@ -// screens/splash_screen.dart (Sudah Lengkap) -import 'package:CCTV_App/main.dart'; -import 'package:flutter/material.dart'; - - -class SplashScreen extends StatefulWidget { - @override - _SplashScreenState createState() => _SplashScreenState(); -} - -class _SplashScreenState extends State { - @override - void initState() { - super.initState(); - print("SplashScreen: initState called."); - _redirect(); - } - - Future _redirect() async { - await Future.delayed(Duration.zero); - if (!mounted) return; - print("SplashScreen: Starting redirection check..."); - - try { - final session = supabase.auth.currentSession; - print("SplashScreen: Current session: ${session?.user.id ?? 'null'}"); - - if (session != null) { - print("SplashScreen: Session found, redirecting to /dashboard"); - Navigator.pushReplacementNamed(context, '/dashboard'); - } else { - print("SplashScreen: No session found, redirecting to /login"); - Navigator.pushReplacementNamed(context, '/login'); - } - } catch (e) { - print("SplashScreen: Error during redirection check: $e"); - Navigator.pushReplacementNamed(context, '/login'); - } - } - - @override - Widget build(BuildContext context) { - print("SplashScreen: Building UI."); - return const Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircularProgressIndicator(color: Colors.teal), - SizedBox(height: 20), - Text("Memuat...", style: TextStyle(color: Colors.teal)), - ], - ), - ), - ); - } -} diff --git a/newata2/lib/screens/video_player_screen.dart b/newata2/lib/screens/video_player_screen.dart deleted file mode 100644 index 9dc07f0..0000000 --- a/newata2/lib/screens/video_player_screen.dart +++ /dev/null @@ -1,152 +0,0 @@ -// screens/video_player_screen.dart (Sudah Lengkap) -import 'package:flutter/material.dart'; -import 'package:video_player/video_player.dart'; -import 'package:chewie/chewie.dart'; - -class VideoPlayerScreen extends StatefulWidget { - final String videoUrl; - const VideoPlayerScreen({super.key, required this.videoUrl}); - - @override - _VideoPlayerScreenState createState() => _VideoPlayerScreenState(); -} - -class _VideoPlayerScreenState extends State { - VideoPlayerController? _videoPlayerController; - ChewieController? _chewieController; - bool _isLoading = true; - String? _errorMessage; - - @override - void initState() { - super.initState(); - print("VideoPlayerScreen: initState called with URL: ${widget.videoUrl}"); - _initializePlayer(); - } - - Future _initializePlayer() async { - if (widget.videoUrl.isEmpty || !Uri.tryParse(widget.videoUrl)!.isAbsolute) { - print("VideoPlayerScreen: Invalid URL provided."); - setState(() { - _isLoading = false; - _errorMessage = "URL Video tidak valid."; - }); - return; - } - try { - print("VideoPlayerScreen: Initializing video controller..."); - _videoPlayerController = - VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)); - await _videoPlayerController!.initialize(); - print("VideoPlayerScreen: Video controller initialized."); - - print("VideoPlayerScreen: Initializing Chewie controller..."); - _chewieController = ChewieController( - videoPlayerController: _videoPlayerController!, - autoPlay: true, - looping: false, - materialProgressColors: ChewieProgressColors( - playedColor: Colors.teal, - handleColor: Colors.teal[300]!, - bufferedColor: Colors.teal[100]!, - backgroundColor: - Colors.blueGrey[600]!, // Sesuaikan warna background progress - ), - placeholder: Container( - color: Colors.black, - child: const Center( - child: CircularProgressIndicator( - color: Colors.white70)), // Buat lebih redup - ), - autoInitialize: true, - // Opsi tambahan: - // showControlsOnInitialize: false, - // allowedScreenSleep: false, // Jaga layar tetap nyala - errorBuilder: (context, errorMessage) { - print( - "VideoPlayerScreen: Chewie errorBuilder: $errorMessage"); // Log error Chewie - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.error_outline, - color: Colors.white70, size: 40), - const SizedBox(height: 8), - const Text('Gagal memutar video', - style: TextStyle(color: Colors.white70)), - Text( - errorMessage, - style: const TextStyle(color: Colors.white54, fontSize: 12), - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ); - }, - ); - print("VideoPlayerScreen: Chewie controller initialized."); - setState(() { - _isLoading = false; - }); - } catch (e, stackTrace) { - print("VideoPlayerScreen: Error initializing video player: $e"); - print(stackTrace); - setState(() { - _isLoading = false; - _errorMessage = "Gagal memuat video."; - }); - } - } - - @override - void dispose() { - print("VideoPlayerScreen: dispose called."); - _videoPlayerController?.dispose(); - _chewieController?.dispose(); // Chewie controller juga perlu di-dispose - super.dispose(); - } - - @override - Widget build(BuildContext context) { - print( - "VideoPlayerScreen: Building UI. isLoading: $_isLoading, error: $_errorMessage"); - return Scaffold( - backgroundColor: Colors.black, - appBar: AppBar( - title: const Text('Pemutar Video'), - backgroundColor: - Colors.black.withOpacity(0.7), // AppBar sedikit transparan - elevation: 0, - ), - body: Center( - child: _isLoading - ? const CircularProgressIndicator(color: Colors.white) - : _errorMessage != null - ? Padding( - padding: const EdgeInsets.all(20.0), - child: Text( - _errorMessage!, - style: const TextStyle(color: Colors.white, fontSize: 16), - textAlign: TextAlign.center, - ), - ) - // Pastikan chewieController tidak null SEBELUM menampilkannya - : _chewieController != null - ? Chewie(controller: _chewieController!) - : const Column( - // Fallback jika chewie controller gagal dibuat - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.error_outline, - color: Colors.white70, size: 40), - SizedBox(height: 10), - Text("Gagal menyiapkan pemutar video.", - style: TextStyle(color: Colors.white70)), - ], - ), - ), - ); - } -} diff --git a/newata2/lib/splash_screen.dart b/newata2/lib/splash_screen.dart new file mode 100644 index 0000000..af158bf --- /dev/null +++ b/newata2/lib/splash_screen.dart @@ -0,0 +1,54 @@ +// ================================================================= +// SPLASH SCREEN PAGE +// ================================================================= + +import 'package:CCTV_App/auth_wrapper.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + @override + void initState() { + super.initState(); + Future.delayed(const Duration(seconds: 3), () { + if (mounted) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const AuthWrapper())); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(CupertinoIcons.lock_shield_fill, + size: 120, color: Colors.tealAccent), + const SizedBox(height: 24), + Text( + 'Smart Anti Theft', + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(fontSize: 28), + ), + const SizedBox(height: 80), + const CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white54), + ), + ], + ), + ), + ); + } +} diff --git a/newata2/macos/Flutter/GeneratedPluginRegistrant.swift b/newata2/macos/Flutter/GeneratedPluginRegistrant.swift index db1fad0..92b6497 100644 --- a/newata2/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/newata2/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,19 +6,13 @@ import FlutterMacOS import Foundation import app_links -import package_info_plus import path_provider_foundation import shared_preferences_foundation import url_launcher_macos -import video_player_avfoundation -import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) - FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) - WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) } diff --git a/newata2/notify-all-user.ts b/newata2/notify-all-user.ts new file mode 100644 index 0000000..8206207 --- /dev/null +++ b/newata2/notify-all-user.ts @@ -0,0 +1,76 @@ +// import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +// import { Resend } from 'https://esm.sh/resend@3.2.0'; +// console.log("Edge Function 'notify-all-users' is initializing."); +// Deno.serve(async (req)=>{ +// try { +// // 1. Ambil data event dari request yang dikirim oleh Trigger Postgres +// const { record } = await req.json(); +// console.log(`New event received for device: ${record.device_id}`); +// // 2. Ambil Kunci API Resend dari environment variables +// const RESEND_API_KEY = Deno.env.get("RESEND_API_KEY"); +// if (!RESEND_API_KEY) { +// throw new Error("RESEND_API_KEY is not set in Supabase secrets."); +// } +// const resend = new Resend(RESEND_API_KEY); +// // 3. Buat Supabase client di dalam Edge Function untuk mengambil daftar email +// const supabaseAdmin = createClient(Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''); +// // 4. Ambil semua email dari tabel 'profiles' +// const { data: profiles, error: profileError } = await supabaseAdmin.from('profiles').select('email'); +// if (profileError) { +// throw new Error(`Failed to fetch profiles: ${profileError.message}`); +// } +// if (!profiles || profiles.length === 0) { +// console.log("No user profiles found to notify."); +// return new Response(JSON.stringify({ +// message: "No users to notify." +// }), { +// headers: { +// "Content-Type": "application/json" +// }, +// status: 200 +// }); +// } +// const emails = profiles.map((p)=>p.email).filter(Boolean); // Ambil email dan filter null/kosong +// console.log(`Found ${emails.length} emails to notify:`, emails); +// // 5. Kirim email ke semua pengguna menggunakan Resend +// const { data, error } = await resend.emails.send({ +// from: 'Sistem Smart Anti Theft ', +// to: emails, +// subject: `⚠️ Peringatan Ancaman: Terdeteksi ${record.event_type} di ${record.location_name}!`, +// html: ` +//

Peringatan Ancaman

+//

Halo,

+//

Sistem kami telah mendeteksi adanya ${record.event_type} pada perangkat ${record.device_id} yang berlokasi di ${record.location_name}.

+//

Sebuah gambar telah berhasil ditangkap sebagai referensi.

+// Captured Image +//

Silakan periksa aplikasi Anda untuk detail lebih lanjut.

+//
+//

Terima kasih,

+//

Tim Keamanan Cerdas Anda

+// ` +// }); +// if (error) { +// console.error("Error sending email:", error); +// throw new Error(JSON.stringify(error)); +// } +// console.log("Successfully sent emails:", data); +// return new Response(JSON.stringify({ +// data +// }), { +// headers: { +// "Content-Type": "application/json" +// }, +// status: 200 +// }); +// } catch (error) { +// console.error("Critical error in Edge Function:", error.message); +// return new Response(JSON.stringify({ +// error: error.message +// }), { +// headers: { +// "Content-Type": "application/json" +// }, +// status: 500 +// }); +// } +// }); diff --git a/newata2/pubspec.lock b/newata2/pubspec.lock index 4085e0b..627e059 100644 --- a/newata2/pubspec.lock +++ b/newata2/pubspec.lock @@ -33,14 +33,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" async: dependency: transitive description: @@ -65,14 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - chewie: - dependency: "direct main" - description: - name: chewie - sha256: "645fbca3f22309381edb5af59a4c8aa544a3d3872d7b7b7c986c2b18b3bdd265" - url: "https://pub.dev" - source: hosted - version: "1.10.0" clock: dependency: transitive description: @@ -97,14 +81,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" - csslib: - dependency: transitive - description: - name: csslib - sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" - source: hosted - version: "1.0.2" cupertino_icons: dependency: "direct main" description: @@ -113,14 +89,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - dbus: - dependency: transitive - description: - name: dbus - sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" - url: "https://pub.dev" - source: hosted - version: "0.7.11" fake_async: dependency: transitive description: @@ -200,14 +168,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - html: - dependency: transitive - description: - name: html - sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" - source: hosted - version: "0.15.6" http: dependency: transitive description: @@ -320,22 +280,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - package_info_plus: - dependency: transitive - description: - name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" - url: "https://pub.dev" - source: hosted - version: "8.3.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" - url: "https://pub.dev" - source: hosted - version: "3.2.0" path: dependency: transitive description: @@ -392,14 +336,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" platform: dependency: transitive description: @@ -669,46 +605,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - video_player: - dependency: "direct main" - description: - name: video_player - sha256: "7d78f0cfaddc8c19d4cb2d3bebe1bfef11f2103b0a03e5398b303a1bf65eeb14" - url: "https://pub.dev" - source: hosted - version: "2.9.5" - video_player_android: - dependency: transitive - description: - name: video_player_android - sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898" - url: "https://pub.dev" - source: hosted - version: "2.7.16" - video_player_avfoundation: - dependency: transitive - description: - name: video_player_avfoundation - sha256: "9ee764e5cd2fc1e10911ae8ad588e1a19db3b6aa9a6eb53c127c42d3a3c3f22f" - url: "https://pub.dev" - source: hosted - version: "2.7.1" - video_player_platform_interface: - dependency: transitive - description: - name: video_player_platform_interface - sha256: df534476c341ab2c6a835078066fc681b8265048addd853a1e3c78740316a844 - url: "https://pub.dev" - source: hosted - version: "6.3.0" - video_player_web: - dependency: transitive - description: - name: video_player_web - sha256: e8bba2e5d1e159d5048c9a491bb2a7b29c535c612bb7d10c1e21107f5bd365ba - url: "https://pub.dev" - source: hosted - version: "2.3.5" vm_service: dependency: transitive description: @@ -717,22 +613,6 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" - wakelock_plus: - dependency: transitive - description: - name: wakelock_plus - sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678 - url: "https://pub.dev" - source: hosted - version: "1.3.2" - wakelock_plus_platform_interface: - dependency: transitive - description: - name: wakelock_plus_platform_interface - sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207 - url: "https://pub.dev" - source: hosted - version: "1.2.3" web: dependency: transitive description: @@ -757,14 +637,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - win32: - dependency: transitive - description: - name: win32 - sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e - url: "https://pub.dev" - source: hosted - version: "5.10.1" xdg_directories: dependency: transitive description: @@ -773,14 +645,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" yet_another_json_isolate: dependency: transitive description: diff --git a/newata2/pubspec.yaml b/newata2/pubspec.yaml index e336ba3..23cc0df 100644 --- a/newata2/pubspec.yaml +++ b/newata2/pubspec.yaml @@ -32,8 +32,8 @@ dependencies: sdk: flutter supabase_flutter: ^2.0.0 # Cek versi terbaru provider: ^6.0.0 # Cek versi terbaru - video_player: ^2.0.0 # Cek versi terbaru - chewie: ^1.5.0 # Cek versi terbaru (sesuaikan dengan video_player) + # video_player: ^2.0.0 # Cek versi terbaru + # chewie: ^1.5.0 # Cek versi terbaru (sesuaikan dengan video_player) flutter_dotenv: ^5.0.0 # Cek versi terbaru intl: ^0.19.0 # Cek versi terbaru (untuk format tanggal/waktu) @@ -65,7 +65,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - .env - # - images/a_dot_burr.jpeg + # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see