Deploy: 2026-05-24 23:27:32

This commit is contained in:
Hamza-Ayed
2026-05-24 23:27:32 +03:00
parent 2ceffc47d9
commit b20f457eaf
156 changed files with 8308 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
import 'dart:convert';
import '../../../core/services/api_service.dart';
import '../../../core/services/secure_storage_service.dart';
import 'models/chatbot_rule_model.dart';
import 'models/contact_model.dart';
import 'models/plan_model.dart';
import 'models/super_admin_stats_model.dart';
import 'models/whatsapp_status_model.dart';
class DashboardRepository {
final ApiService _apiService = ApiService();
final SecureStorageService _secureStorage = SecureStorageService();
// English: Helper to retrieve token from secure storage. Throws exception if token is missing.
// Arabic: دالة مساعدة لاسترداد الرمز من التخزين الآمن. تطلق استثناء إذا كان الرمز مفقودًا.
Future<String> _getToken() async {
final token = await _secureStorage.readToken();
if (token == null || token.isEmpty) {
throw Exception('أنت غير مصرح لك بالوصول، يرجى إعادة تسجيل الدخول');
}
return token;
}
// English: Load the active WhatsApp connection session status.
// Arabic: تحميل حالة اتصال جلسة الواتساب النشطة.
Future<WhatsAppStatusModel?> getWhatsAppStatus() async {
final token = await _getToken();
final response = await _apiService.getWhatsAppStatus(token);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final statusData = decoded['data'] as Map<String, dynamic>?;
if (statusData == null) return null;
return WhatsAppStatusModel.fromJson(statusData);
} else {
throw Exception('فشل في جلب حالة الواتساب');
}
}
// English: Load available subscription plans.
// Arabic: تحميل باقات الاشتراك المتاحة.
Future<List<PlanModel>> getPlans() async {
final token = await _getToken();
final response = await _apiService.getPlans(token);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final list = decoded['data'] as List<dynamic>? ?? [];
return list.map((item) => PlanModel.fromJson(item as Map<String, dynamic>)).toList();
} else {
throw Exception('فشل في جلب الباقات المتاحة');
}
}
// English: Load CRM contacts directory.
// Arabic: تحميل دليل جهات اتصال إدارة العملاء.
Future<List<ContactModel>> getContacts() async {
final token = await _getToken();
final response = await _apiService.getContacts(token);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final list = decoded['data'] as List<dynamic>? ?? [];
return list.map((item) => ContactModel.fromJson(item as Map<String, dynamic>)).toList();
} else {
throw Exception('فشل في جلب جهات الاتصال');
}
}
// English: Create a new contact inside remote CRM directory.
// Arabic: إنشاء جهة اتصال جديدة داخل دليل إدارة العملاء البعيد.
Future<void> addContact(String name, String phone) async {
final token = await _getToken();
final response = await _apiService.addContact(token, name, phone);
if (response.statusCode == 201 || response.statusCode == 200) {
return;
} else if (response.statusCode == 409) {
throw Exception('رقم الهاتف هذا مسجل بالفعل في جهات الاتصال الخاصة بك');
} else {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final error = decoded['message'] as String? ?? 'فشل في إضافة جهة الاتصال';
throw Exception(error);
}
}
// English: Load chatbot auto-reply rules.
// Arabic: تحميل قواعد الرد الآلي لروبوت الدردشة.
Future<List<ChatbotRuleModel>> getChatbotRules() async {
final token = await _getToken();
final response = await _apiService.getChatbotRules(token);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final list = decoded['data'] as List<dynamic>? ?? [];
return list.map((item) => ChatbotRuleModel.fromJson(item as Map<String, dynamic>)).toList();
} else {
throw Exception('فشل في جلب قواعد الرد الآلي');
}
}
// English: Load platform statistics (Super Admin only).
// Arabic: تحميل إحصائيات المنصة (للمشرف العام فقط).
Future<SuperAdminStatsModel> getAdminStats() async {
final token = await _getToken();
final response = await _apiService.getAdminStats(token);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final statsData = decoded['data'] as Map<String, dynamic>?;
if (statsData == null) {
throw Exception('البيانات غير صالحة');
}
return SuperAdminStatsModel.fromJson(decoded['data'] as Map<String, dynamic>);
} else {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final error = decoded['error'] as String? ?? 'فشل في جلب إحصائيات المشرف العام';
throw Exception(error);
}
}
// English: Request a new WhatsApp QR connection.
// Arabic: طلب اتصال واتساب جديد برمز استجابة سريع.
Future<void> requestWhatsAppQr() async {
final token = await _getToken();
final response = await _apiService.requestWhatsAppQr(token);
if (response.statusCode != 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final error = decoded['message'] as String? ?? 'فشل في طلب رمز الاستجابة السريعة';
throw Exception(error);
}
}
// English: Disconnect the active WhatsApp connection.
// Arabic: قطع اتصال الواتساب النشط.
Future<void> disconnectWhatsApp() async {
final token = await _getToken();
final response = await _apiService.disconnectWhatsApp(token);
if (response.statusCode != 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final error = decoded['message'] as String? ?? 'فشل في قطع الاتصال';
throw Exception(error);
}
}
// English: Approve a pending company subscription.
// Arabic: الموافقة على اشتراك شركة معلق.
Future<void> approveBilling(int companyId) async {
final token = await _getToken();
final response = await _apiService.approveBilling(token, companyId);
if (response.statusCode != 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final error = decoded['error'] as String? ?? 'فشل في الموافقة على الاشتراك';
throw Exception(error);
}
}
}

View File

@@ -0,0 +1,72 @@
import 'package:equatable/equatable.dart';
class ChatbotRuleModel extends Equatable {
// English: Unique identifier for the rule.
// Arabic: المعرف الفريد للقاعدة.
final int id;
// English: The ID of the company this rule belongs to.
// Arabic: معرف الشركة التي تنتمي إليها هذه القاعدة.
final int companyId;
// English: The session ID this rule is bound to.
// Arabic: معرف الجلسة المرتبطة بها هذه القاعدة.
final int? sessionId;
// English: Trigger type (keyword, gemini_ai).
// Arabic: نوع المشغل (كلمة مفتاحية، ذكاء اصطناعي).
final String triggerType;
// English: The comma-separated static reply keywords.
// Arabic: الكلمات المفتاحية للردود الثابتة مفصولة بفاصلة.
final String? keyword;
// English: Predefined reply content or Gemini prompt instructions.
// Arabic: محتوى الرد المحدد مسبقاً أو تعليمات موجه الذكاء الاصطناعي.
final String? aiPrompt;
// English: Active state flag.
// Arabic: علم الحالة النشطة.
final bool isActive;
const ChatbotRuleModel({
required this.id,
required this.companyId,
this.sessionId,
required this.triggerType,
this.keyword,
this.aiPrompt,
required this.isActive,
});
// English: Factory constructor to parse ChatbotRuleModel from JSON data map.
// Arabic: منشئ المصنع لتحليل نموذج قاعدة الرد الآلي من خريطة بيانات جيسون.
factory ChatbotRuleModel.fromJson(Map<String, dynamic> json) {
return ChatbotRuleModel(
id: json['id'] as int? ?? 0,
companyId: json['company_id'] as int? ?? 0,
sessionId: json['session_id'] as int?,
triggerType: json['trigger_type'] as String? ?? 'keyword',
keyword: json['keyword'] as String?,
aiPrompt: json['ai_prompt'] as String?,
isActive: (json['is_active'] as int? ?? 0) == 1,
);
}
// English: Convert model to JSON map.
// Arabic: تحويل النموذج إلى خريطة جيسون.
Map<String, dynamic> toJson() {
return {
'id': id,
'company_id': companyId,
'session_id': sessionId,
'trigger_type': triggerType,
'keyword': keyword,
'ai_prompt': aiPrompt,
'is_active': isActive ? 1 : 0,
};
}
@override
List<Object?> get props => [id, companyId, sessionId, triggerType, keyword, aiPrompt, isActive];
}

View File

@@ -0,0 +1,58 @@
import 'package:equatable/equatable.dart';
class ContactModel extends Equatable {
// English: Unique identifier for the contact.
// Arabic: المعرف الفريد لجهة الاتصال.
final int id;
// English: Name of the contact.
// Arabic: اسم جهة الاتصال.
final String name;
// English: Phone number.
// Arabic: رقم الهاتف.
final String phone;
// English: Optional email address.
// Arabic: البريد الإلكتروني الاختياري.
final String? email;
// English: Optional descriptive notes.
// Arabic: ملاحظات وصفية اختيارية.
final String? notes;
const ContactModel({
required this.id,
required this.name,
required this.phone,
this.email,
this.notes,
});
// English: Factory constructor to parse ContactModel from JSON map.
// Arabic: منشئ المصنع لتحليل نموذج جهة الاتصال من خريطة جيسون.
factory ContactModel.fromJson(Map<String, dynamic> json) {
return ContactModel(
id: json['id'] as int? ?? 0,
name: json['name'] as String? ?? '',
phone: json['phone'] as String? ?? '',
email: json['email'] as String?,
notes: json['notes'] as String?,
);
}
// English: Convert model to JSON map.
// Arabic: تحويل النموذج إلى خريطة جيسون.
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'phone': phone,
'email': email,
'notes': notes,
};
}
@override
List<Object?> get props => [id, name, phone, email, notes];
}

View File

@@ -0,0 +1,44 @@
import 'package:equatable/equatable.dart';
class PlanModel extends Equatable {
// English: Unique plan identifier.
// Arabic: المعرف الفريد للباقة.
final int id;
// English: Name of the plan (e.g. Free Trial, Pro, Enterprise).
// Arabic: اسم الباقة (مثل التجريبية، الاحترافية، الشركات).
final String name;
// English: Monthly price of the plan.
// Arabic: السعر الشهري للباقة.
final double price;
const PlanModel({
required this.id,
required this.name,
required this.price,
});
// English: Factory constructor to parse PlanModel from JSON data map.
// Arabic: منشئ المصنع لتحليل نموذج الباقة من خريطة بيانات جيسون.
factory PlanModel.fromJson(Map<String, dynamic> json) {
return PlanModel(
id: json['id'] as int? ?? 0,
name: json['name'] as String? ?? 'Nabeh Plan',
price: (json['price'] as num?)?.toDouble() ?? 0.0,
);
}
// English: Convert model to JSON map.
// Arabic: تحويل النموذج إلى خريطة جيسون.
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'price': price,
};
}
@override
List<Object?> get props => [id, name, price];
}

View File

@@ -0,0 +1,56 @@
import 'package:equatable/equatable.dart';
class SuperAdminStatsModel extends Equatable {
// English: Overall platform stats.
// Arabic: إحصائيات المنصة الإجمالية.
final int totalCompanies;
final int totalSessions;
final int connectedSessions;
// English: Lists of active companies, pending billing approvals, and subscription plans.
// Arabic: قوائم بالشركات النشطة، وموافقات الفواتير المعلقة، وباقات الاشتراك المتاحة.
final List<dynamic> companies;
final List<dynamic> pendingApprovals;
final List<dynamic> plans;
const SuperAdminStatsModel({
required this.totalCompanies,
required this.totalSessions,
required this.connectedSessions,
required this.companies,
required this.pendingApprovals,
required this.plans,
});
// English: Factory constructor to parse SuperAdminStatsModel from JSON data map.
// Arabic: منشئ المصنع لتحليل نموذج إحصائيات المشرف العام من خريطة بيانات جيسون.
factory SuperAdminStatsModel.fromJson(Map<String, dynamic> json) {
final stats = json['stats'] as Map<String, dynamic>? ?? {};
return SuperAdminStatsModel(
totalCompanies: stats['total_companies'] as int? ?? 0,
totalSessions: stats['total_sessions'] as int? ?? 0,
connectedSessions: stats['connected_sessions'] as int? ?? 0,
companies: json['companies'] as List<dynamic>? ?? [],
pendingApprovals: json['pending_approvals'] as List<dynamic>? ?? [],
plans: json['plans'] as List<dynamic>? ?? [],
);
}
// English: Convert model to JSON map.
// Arabic: تحويل النموذج إلى خريطة جيسون.
Map<String, dynamic> toJson() {
return {
'stats': {
'total_companies': totalCompanies,
'total_sessions': totalSessions,
'connected_sessions': connectedSessions,
},
'companies': companies,
'pending_approvals': pendingApprovals,
'plans': plans,
};
}
@override
List<Object?> get props => [totalCompanies, totalSessions, connectedSessions, companies, pendingApprovals, plans];
}

View File

@@ -0,0 +1,65 @@
import 'package:equatable/equatable.dart';
class WhatsAppStatusModel extends Equatable {
// English: Unique session identifier in database.
// Arabic: معرف الجلسة الفريد في قاعدة البيانات.
final int id;
// English: Name of the session (e.g. support, sales).
// Arabic: اسم الجلسة (مثل الدعم، المبيعات).
final String name;
// English: Unique session key sent to Baileys Node.js gateway.
// Arabic: مفتاح الجلسة الفريد المرسل إلى بوابة الجيسون الخاصة بالواتساب.
final String sessionKey;
// English: Current connection status (waiting_qr, connected, disconnected).
// Arabic: حالة الاتصال الحالية (بانتظار رمز الاستجابة، متصل، غير متصل).
final String status;
// English: Base64 or text QR code from Baileys if waiting for scan.
// Arabic: رمز الاستجابة السريعة (QR) من البوابة إذا كان بانتظار المسح.
final String? qrCode;
// English: Linked phone number on success connection.
// Arabic: رقم الهاتف المرتبط بالجلسة عند الاتصال بنجاح.
final String? phone;
const WhatsAppStatusModel({
required this.id,
required this.name,
required this.sessionKey,
required this.status,
this.qrCode,
this.phone,
});
// English: Factory constructor to parse WhatsApp status session model from JSON.
// Arabic: منشئ المصنع لتحليل نموذج حالة جلسة الواتساب من استجابة جيسون.
factory WhatsAppStatusModel.fromJson(Map<String, dynamic> json) {
return WhatsAppStatusModel(
id: json['id'] as int? ?? 0,
name: json['name'] as String? ?? 'WhatsApp Team',
sessionKey: json['session_key'] as String? ?? '',
status: json['status'] as String? ?? 'disconnected',
qrCode: json['qr_code'] as String?,
phone: json['phone'] as String?,
);
}
// English: Convert model to JSON payload.
// Arabic: تحويل النموذج إلى حمولة جيسون.
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'session_key': sessionKey,
'status': status,
'qr_code': qrCode,
'phone': phone,
};
}
@override
List<Object?> get props => [id, name, sessionKey, status, qrCode, phone];
}