MIF_E31222656/lib/screens/community/services/guide_service.dart

251 lines
8.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:tugas_akhir_supabase/screens/community/models/farming_guide_model.dart';
import 'package:tugas_akhir_supabase/screens/community/data/static_guides_data.dart';
import 'package:tugas_akhir_supabase/screens/community/utils/plant_categorizer.dart';
class GuideService {
final _supabase = Supabase.instance.client;
// Storage bucket that should be used for images
String _imageBucket = 'images';
bool _bucketsChecked = false;
// Singleton pattern
static final GuideService _instance = GuideService._internal();
factory GuideService() => _instance;
GuideService._internal() {
// Check available buckets
_checkAvailableBuckets();
// Debug existing guides when service is created
debugCheckExistingGuides();
}
// Check available buckets and set the appropriate one to use
Future<void> _checkAvailableBuckets() async {
if (_bucketsChecked) return;
try {
final buckets = await _supabase.storage.listBuckets();
final bucketNames = buckets.map((b) => b.name).toList();
debugPrint('Available buckets: ${bucketNames.join(', ')}');
// Preferred bucket order: images, guide_images, avatars
if (bucketNames.contains('images')) {
_imageBucket = 'images';
} else if (bucketNames.contains('guide_images')) {
_imageBucket = 'guide_images';
} else if (bucketNames.contains('avatars')) {
_imageBucket = 'avatars';
} else if (bucketNames.isNotEmpty) {
_imageBucket = bucketNames.first;
}
debugPrint('Selected bucket for guide images: $_imageBucket');
_bucketsChecked = true;
} catch (e) {
debugPrint('Error checking buckets: $e');
}
}
// Debug function to check existing guides and their image URLs
Future<void> debugCheckExistingGuides() async {
try {
await _checkAvailableBuckets();
final response = await _supabase
.from('farming_guides')
.select('*')
.timeout(const Duration(seconds: 5));
debugPrint('====== DEBUG EXISTING GUIDES ======');
debugPrint('Found ${response.length} guides in database');
debugPrint('Using bucket: $_imageBucket');
// Check each guide and its image URL
for (var i = 0; i < response.length; i++) {
final guide = response[i];
final id = guide['id'] ?? 'unknown';
final title = guide['title'] ?? 'No title';
final imageUrl = guide['image_url'];
debugPrint('Guide #${i + 1}: $title (ID: $id)');
if (imageUrl == null) {
debugPrint(' - No image URL (null)');
} else if (imageUrl.isEmpty) {
debugPrint(' - Empty image URL');
} else {
debugPrint(' - Original image URL: $imageUrl');
final fixedUrl = fixImageUrl(imageUrl);
debugPrint(' - Fixed image URL: $fixedUrl');
// Try to fix the guide's image URL in the database if needed
if (fixedUrl != null && fixedUrl != imageUrl) {
try {
await _supabase
.from('farming_guides')
.update({'image_url': fixedUrl})
.eq('id', id);
debugPrint(' - Updated image URL in database');
} catch (e) {
debugPrint(' - Failed to update image URL: $e');
}
}
}
}
debugPrint('====== END DEBUG ======');
} catch (e) {
debugPrint('Error checking existing guides: $e');
}
}
// Mengambil panduan dari database dan menggabungkan dengan data statis
Future<List<FarmingGuideModel>> getGuides() async {
try {
// Mencoba mengambil data dari Supabase dengan timeout 5 detik
final response = await _supabase
.from('farming_guides')
.select('*')
.order('created_at', ascending: false)
.timeout(const Duration(seconds: 5));
debugPrint('Loaded ${response.length} guides from database');
// Cek respons untuk masalah pada data gambar
for (final guide in response) {
if (guide['image_url'] == null) {
debugPrint('Guide with title "${guide['title']}" has null image_url');
} else {
debugPrint('Guide image URL: ${guide['image_url']}');
}
}
// Konversi ke List<FarmingGuideModel> dengan auto-kategorisasi jika perlu
final dbGuides =
List<Map<String, dynamic>>.from(response).map((map) {
// Jika kategori kosong atau generic, coba kategorikan otomatis
final currentCategory = map['category'] ?? '';
if (currentCategory.isEmpty ||
currentCategory.toLowerCase() == 'umum') {
final title = map['title'] ?? '';
final content = map['content'] ?? '';
// Auto-kategorisasi berdasarkan judul dan konten
final category = PlantCategorizer.categorize(
title,
description: content,
);
map['category'] = category;
debugPrint('Auto-categorized "${title}" as "$category"');
}
// Cek dan perbaiki URL gambar
if (map['image_url'] != null) {
map['image_url'] = fixImageUrl(map['image_url']);
}
return FarmingGuideModel.fromMap(map);
}).toList();
// Mendapatkan data statis
final staticGuides = StaticGuidesData().getAllGuides();
// Gabungkan keduanya
final allGuides = [...dbGuides, ...staticGuides];
// Hilangkan duplikat berdasarkan judul
final uniqueTitles = <String>{};
final uniqueGuides = <FarmingGuideModel>[];
for (final guide in allGuides) {
if (uniqueTitles.add(guide.title)) {
uniqueGuides.add(guide);
}
}
return uniqueGuides;
} catch (e) {
// Jika terjadi error, gunakan data statis saja
debugPrint('Error loading guides from database: $e');
debugPrint('Falling back to static data only');
return StaticGuidesData().getAllGuides();
}
}
// Mengambil panduan berdasarkan kategori
Future<List<FarmingGuideModel>> getGuidesByCategory(String category) async {
if (category.isEmpty) {
return getGuides();
}
try {
// Mendapatkan semua panduan terlebih dahulu
final allGuides = await getGuides();
// Filter berdasarkan kategori
// Ubah pencocokan menjadi case-insensitive dan juga menerima partial match
return allGuides.where((guide) {
// Check if the category matches (case insensitive)
if (guide.category.toLowerCase() == category.toLowerCase()) {
return true;
}
// Check if it's a partial match (for better categorization)
if (guide.category.toLowerCase().contains(category.toLowerCase()) ||
category.toLowerCase().contains(guide.category.toLowerCase())) {
return true;
}
return false;
}).toList();
} catch (e) {
// Jika terjadi error, gunakan data statis saja
debugPrint('Error filtering guides by category: $e');
return StaticGuidesData().getGuidesByCategory(category);
}
}
// Memperbaiki URL gambar jika perlu
String? fixImageUrl(String? imageUrl) {
if (imageUrl == null || imageUrl.isEmpty) {
return null;
}
// Log untuk debugging
debugPrint('Original image URL: $imageUrl');
// Fix URL jika perlu (pastikan URL lengkap)
if (!imageUrl.startsWith('http')) {
// Jika URL tidak lengkap, gunakan Storage dari Supabase
String bucketName = _imageBucket; // Use the detected bucket
String fileName = imageUrl;
// Jika imageUrl sudah mengandung nama bucket, ekstrak
if (imageUrl.contains('/')) {
final parts = imageUrl.split('/');
if (parts.length >= 2) {
bucketName = parts[0];
fileName = parts.sublist(1).join('/');
}
}
// Dapatkan URL publik yang valid
try {
final fixedUrl = _supabase.storage
.from(bucketName)
.getPublicUrl(fileName);
debugPrint('Fixed image URL: $fixedUrl');
return fixedUrl;
} catch (e) {
debugPrint('Error fixing image URL: $e');
return imageUrl; // Kembalikan URL asli jika gagal
}
}
return imageUrl;
}
}