143 lines
3.7 KiB
Dart
143 lines
3.7 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
import 'supabase_provider.dart';
|
|
import '../utils/app_time.dart';
|
|
|
|
/// Admin user query parameters for server-side pagination.
|
|
class AdminUserQuery {
|
|
/// Creates admin user query parameters.
|
|
const AdminUserQuery({
|
|
this.offset = 0,
|
|
this.limit = 50,
|
|
this.searchQuery = '',
|
|
});
|
|
|
|
/// Offset for pagination.
|
|
final int offset;
|
|
|
|
/// Number of items per page (default: 50).
|
|
final int limit;
|
|
|
|
/// Full text search query.
|
|
final String searchQuery;
|
|
|
|
AdminUserQuery copyWith({
|
|
int? offset,
|
|
int? limit,
|
|
String? searchQuery,
|
|
}) {
|
|
return AdminUserQuery(
|
|
offset: offset ?? this.offset,
|
|
limit: limit ?? this.limit,
|
|
searchQuery: searchQuery ?? this.searchQuery,
|
|
);
|
|
}
|
|
}
|
|
|
|
final adminUserQueryProvider = StateProvider<AdminUserQuery>((ref) => const AdminUserQuery());
|
|
|
|
final adminUserControllerProvider = Provider<AdminUserController>((ref) {
|
|
final client = ref.watch(supabaseClientProvider);
|
|
return AdminUserController(client);
|
|
});
|
|
|
|
class AdminUserStatus {
|
|
AdminUserStatus({required this.email, required this.bannedUntil});
|
|
|
|
final String? email;
|
|
final DateTime? bannedUntil;
|
|
|
|
bool get isLocked {
|
|
if (bannedUntil == null) return false;
|
|
return bannedUntil!.isAfter(AppTime.now());
|
|
}
|
|
}
|
|
|
|
class AdminUserController {
|
|
AdminUserController(this._client);
|
|
|
|
final SupabaseClient _client;
|
|
|
|
Future<void> updateProfile({
|
|
required String userId,
|
|
required String fullName,
|
|
required String role,
|
|
}) async {
|
|
await _client
|
|
.from('profiles')
|
|
.update({'full_name': fullName, 'role': role})
|
|
.eq('id', userId);
|
|
}
|
|
|
|
Future<void> updateRole({
|
|
required String userId,
|
|
required String role,
|
|
}) async {
|
|
await _client.from('profiles').update({'role': role}).eq('id', userId);
|
|
}
|
|
|
|
Future<void> setPassword({
|
|
required String userId,
|
|
required String password,
|
|
}) async {
|
|
await _invokeAdminFunction(
|
|
action: 'set_password',
|
|
payload: {'userId': userId, 'password': password},
|
|
);
|
|
}
|
|
|
|
Future<void> setLock({required String userId, required bool locked}) async {
|
|
await _invokeAdminFunction(
|
|
action: 'set_lock',
|
|
payload: {'userId': userId, 'locked': locked},
|
|
);
|
|
}
|
|
|
|
Future<AdminUserStatus> fetchStatus(String userId) async {
|
|
final data = await _invokeAdminFunction(
|
|
action: 'get_user',
|
|
payload: {'userId': userId},
|
|
);
|
|
final user = (data as Map<String, dynamic>)['user'] as Map<String, dynamic>;
|
|
final bannedUntilRaw = user['banned_until'] as String?;
|
|
final bannedUntilParsed = bannedUntilRaw == null
|
|
? null
|
|
: DateTime.tryParse(bannedUntilRaw);
|
|
return AdminUserStatus(
|
|
email: user['email'] as String?,
|
|
bannedUntil: bannedUntilParsed == null
|
|
? null
|
|
: AppTime.toAppTime(bannedUntilParsed),
|
|
);
|
|
}
|
|
|
|
Future<dynamic> _invokeAdminFunction({
|
|
required String action,
|
|
required Map<String, dynamic> payload,
|
|
}) async {
|
|
final response = await _client.functions.invoke(
|
|
'admin_user_management',
|
|
body: {'action': action, ...payload},
|
|
);
|
|
if (response.status != 200) {
|
|
throw Exception(_extractErrorMessage(response.data));
|
|
}
|
|
return response.data;
|
|
}
|
|
|
|
String _extractErrorMessage(dynamic data) {
|
|
if (data is Map<String, dynamic>) {
|
|
final error = data['error'];
|
|
if (error is String && error.trim().isNotEmpty) {
|
|
return error;
|
|
}
|
|
final message = data['message'];
|
|
if (message is String && message.trim().isNotEmpty) {
|
|
return message;
|
|
}
|
|
}
|
|
return 'Admin request failed.';
|
|
}
|
|
}
|