petitspas/frontend/lib/services/configuration_service.dart
Julien Martin 1834eb8c79 feat(admin): panneau Paramètres - sauvegarde config + test SMTP
- Onglet Paramètres dans l'admin avec 3 sections (Email, Personnalisation, Avancé)
- Service ConfigurationService (GET config, PATCH bulk, POST test-smtp)
- Bouton Sauvegarder et bouton Tester SMTP (sauvegarde avant test)
- Endpoints api_config pour configuration

Closes #15

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 11:16:37 +01:00

140 lines
4.6 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'api/api_config.dart';
import 'api/tokenService.dart';
/// Réponse GET /configuration (liste complète)
class ConfigItem {
final String cle;
final String? valeur;
final String type;
final String? categorie;
final String? description;
ConfigItem({
required this.cle,
this.valeur,
required this.type,
this.categorie,
this.description,
});
factory ConfigItem.fromJson(Map<String, dynamic> json) {
return ConfigItem(
cle: json['cle'] as String,
valeur: json['valeur'] as String?,
type: json['type'] as String? ?? 'string',
categorie: json['categorie'] as String?,
description: json['description'] as String?,
);
}
}
/// Réponse GET /configuration/:category (objet clé -> { value, type, description })
class ConfigValueItem {
final dynamic value;
final String type;
final String? description;
ConfigValueItem({required this.value, required this.type, this.description});
factory ConfigValueItem.fromJson(Map<String, dynamic> json) {
return ConfigValueItem(
value: json['value'],
type: json['type'] as String? ?? 'string',
description: json['description'] as String?,
);
}
}
class ConfigurationService {
static Future<Map<String, String>> _headers() async {
final token = await TokenService.getToken();
return token != null
? ApiConfig.authHeaders(token)
: Map<String, String>.from(ApiConfig.headers);
}
/// GET /api/v1/configuration/setup/status
static Future<bool> getSetupStatus() async {
final response = await http.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configurationSetupStatus}'),
headers: await _headers(),
);
if (response.statusCode != 200) return true;
final data = jsonDecode(response.body);
return data['data']?['setupCompleted'] as bool? ?? true;
}
/// GET /api/v1/configuration (toutes les configs)
static Future<List<ConfigItem>> getAll() async {
final response = await http.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configuration}'),
headers: await _headers(),
);
if (response.statusCode != 200) {
throw Exception(
(jsonDecode(response.body) as Map)['message'] ?? 'Erreur chargement configuration',
);
}
final data = jsonDecode(response.body);
final list = data['data'] as List<dynamic>? ?? [];
return list.map((e) => ConfigItem.fromJson(e as Map<String, dynamic>)).toList();
}
/// GET /api/v1/configuration/:category
static Future<Map<String, ConfigValueItem>> getByCategory(String category) async {
final response = await http.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configuration}/$category'),
headers: await _headers(),
);
if (response.statusCode != 200) {
throw Exception(
(jsonDecode(response.body) as Map)['message'] ?? 'Erreur chargement configuration',
);
}
final data = jsonDecode(response.body);
final map = data['data'] as Map<String, dynamic>? ?? {};
return map.map((k, v) => MapEntry(k, ConfigValueItem.fromJson(v as Map<String, dynamic>)));
}
/// PATCH /api/v1/configuration/bulk
static Future<void> updateBulk(Map<String, dynamic> body) async {
final response = await http.patch(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configurationBulk}'),
headers: await _headers(),
body: jsonEncode(body),
);
if (response.statusCode != 200) {
final err = jsonDecode(response.body) as Map;
throw Exception(err['message'] ?? 'Erreur lors de la sauvegarde');
}
}
/// POST /api/v1/configuration/test-smtp
static Future<String> testSmtp(String testEmail) async {
final response = await http.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configurationTestSmtp}'),
headers: await _headers(),
body: jsonEncode({'testEmail': testEmail}),
);
final data = jsonDecode(response.body) as Map;
if (response.statusCode == 200 && data['success'] == true) {
return data['message'] as String? ?? 'Test SMTP réussi.';
}
throw Exception(data['error'] ?? data['message'] ?? 'Échec du test SMTP');
}
/// POST /api/v1/configuration/setup/complete (après première config)
static Future<void> completeSetup() async {
final response = await http.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.configurationSetupComplete}'),
headers: await _headers(),
);
if (response.statusCode != 200) {
final err = jsonDecode(response.body) as Map;
throw Exception(err['message'] ?? 'Erreur finalisation configuration');
}
}
}