MIF_E31222656/lib/screens/calendar/fix_fields_table.dart

244 lines
8.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
/// Utility class to fix the fields table schema
class FixFieldsTableUtil {
/// Fix the fields table schema
static Future<void> fixFieldsTable(BuildContext context) async {
try {
// Show loading dialog
showDialog(
context: context,
barrierDismissible: false,
builder:
(context) => const AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Memperbaiki struktur tabel fields...'),
],
),
),
);
final client = Supabase.instance.client;
// Clear cache first
await _clearSupabaseCache();
// Step 1: Check if updated_at column exists
final columnsResult = await client
.rpc(
'check_column_exists',
params: {'table_name': 'fields', 'column_name': 'updated_at'},
)
.timeout(const Duration(seconds: 10));
print('Column check result: $columnsResult');
// Step 2: Add updated_at column if it doesn't exist
if (columnsResult == false) {
await client
.rpc(
'execute_sql',
params: {
'sql_statement':
'ALTER TABLE public.fields ADD COLUMN updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()',
},
)
.timeout(const Duration(seconds: 10));
print('Added updated_at column');
}
// Step 3: Create or replace the update trigger function
await client
.rpc(
'execute_sql',
params: {
'sql_statement':
'CREATE OR REPLACE FUNCTION update_fields_updated_at() RETURNS TRIGGER AS \$\$ BEGIN NEW.updated_at = now(); RETURN NEW; END; \$\$ LANGUAGE plpgsql;',
},
)
.timeout(const Duration(seconds: 10));
print('Created trigger function');
// Step 4: Create the trigger if it doesn't exist
await client
.rpc(
'execute_sql',
params: {
'sql_statement':
'DROP TRIGGER IF EXISTS trigger_fields_updated_at ON public.fields; CREATE TRIGGER trigger_fields_updated_at BEFORE UPDATE ON public.fields FOR EACH ROW EXECUTE PROCEDURE update_fields_updated_at();',
},
)
.timeout(const Duration(seconds: 10));
print('Created trigger');
// Step 5: Update all fields to set updated_at = created_at where null
await client
.rpc(
'execute_sql',
params: {
'sql_statement':
'UPDATE public.fields SET updated_at = created_at WHERE updated_at IS NULL',
},
)
.timeout(const Duration(seconds: 10));
print('Updated null updated_at values');
// Step 6: Fix RLS policies to avoid recursion issues
await _fixRLSPolicies(client);
// Close the dialog
Navigator.pop(context);
// Show success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Struktur tabel fields berhasil diperbaiki'),
backgroundColor: Colors.green,
),
);
} catch (e) {
print('Error fixing fields table: $e');
// Close the dialog if it's open
Navigator.pop(context);
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Gagal memperbaiki struktur tabel fields: ${e.toString()}',
),
backgroundColor: Colors.red,
),
);
}
}
/// Fix RLS policies to avoid recursion issues
static Future<void> _fixRLSPolicies(SupabaseClient client) async {
try {
// Fix user_roles policies
await client
.rpc(
'execute_sql',
params: {
'sql_statement': '''
-- Remove existing policies from user_roles
DROP POLICY IF EXISTS "Users can view their own roles" ON public.user_roles;
DROP POLICY IF EXISTS "Users can insert their own roles" ON public.user_roles;
DROP POLICY IF EXISTS "Users can update their own roles" ON public.user_roles;
DROP POLICY IF EXISTS "Users can delete their own roles" ON public.user_roles;
-- Create simplified policies for user_roles
CREATE POLICY "Enable read access for authenticated users"
ON public.user_roles FOR SELECT
USING (auth.role() = 'authenticated');
CREATE POLICY "Enable insert access for authenticated users"
ON public.user_roles FOR INSERT
WITH CHECK (auth.role() = 'authenticated');
CREATE POLICY "Enable update for users based on user_id"
ON public.user_roles FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Enable delete for users based on user_id"
ON public.user_roles FOR DELETE
USING (auth.uid() = user_id);
''',
},
)
.timeout(const Duration(seconds: 10));
// Fix fields policies
await client
.rpc(
'execute_sql',
params: {
'sql_statement': '''
-- Remove existing policies from fields
DROP POLICY IF EXISTS "Users can view their own fields" ON public.fields;
DROP POLICY IF EXISTS "Users can insert their own fields" ON public.fields;
DROP POLICY IF EXISTS "Users can update their own fields" ON public.fields;
DROP POLICY IF EXISTS "Users can delete their own fields" ON public.fields;
-- Create simplified policies for fields
CREATE POLICY "Enable read access for authenticated users"
ON public.fields FOR SELECT
USING (auth.role() = 'authenticated');
CREATE POLICY "Enable insert access for authenticated users"
ON public.fields FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Enable update for users based on user_id"
ON public.fields FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Enable delete for users based on user_id"
ON public.fields FOR DELETE
USING (auth.uid() = user_id);
''',
},
)
.timeout(const Duration(seconds: 10));
print('Fixed RLS policies');
} catch (e) {
print('Error fixing RLS policies: $e');
rethrow;
}
}
/// Clear Supabase cache to help with stuck issues
static Future<void> _clearSupabaseCache() async {
try {
// Clear SharedPreferences cache
final prefs = await SharedPreferences.getInstance();
final keys =
prefs
.getKeys()
.where(
(key) =>
key.startsWith('supabase') ||
key.contains('auth') ||
key.contains('fields') ||
key.contains('cache'),
)
.toList();
for (var key in keys) {
await prefs.remove(key);
}
print('Cleared ${keys.length} cache entries');
// Force refresh auth session
try {
await Supabase.instance.client.auth.refreshSession();
print('Auth session refreshed');
} catch (e) {
print('Error refreshing auth session: $e');
// Continue even if this fails
}
} catch (e) {
print('Error clearing cache: $e');
// Continue even if this fails
}
}
/// Public method to clear cache
static Future<void> clearCache() async {
await _clearSupabaseCache();
}
}