Update: 2026-05-08 14:11:53
This commit is contained in:
@@ -306,7 +306,6 @@ class CompaniesManagementView extends StatelessWidget {
|
|||||||
void _showLinkJoFotaraDialog(BuildContext context, Map<String, dynamic> company, CompaniesManagementController controller) {
|
void _showLinkJoFotaraDialog(BuildContext context, Map<String, dynamic> company, CompaniesManagementController controller) {
|
||||||
final clientIdC = TextEditingController();
|
final clientIdC = TextEditingController();
|
||||||
final secretKeyC = TextEditingController();
|
final secretKeyC = TextEditingController();
|
||||||
final sequenceC = TextEditingController();
|
|
||||||
|
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -319,7 +318,6 @@ class CompaniesManagementView extends StatelessWidget {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_editField('Client ID', clientIdC, Icons.vpn_key),
|
_editField('Client ID', clientIdC, Icons.vpn_key),
|
||||||
_editField('Secret Key', secretKeyC, Icons.lock),
|
_editField('Secret Key', secretKeyC, Icons.lock),
|
||||||
_editField('Income Source Sequence (اختياري)', sequenceC, Icons.format_list_numbered),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -332,7 +330,7 @@ class CompaniesManagementView extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Get.back();
|
Get.back();
|
||||||
controller.connectJoFotara(company['id'], clientIdC.text, secretKeyC.text, sequenceC.text);
|
controller.connectJoFotara(company['id'], clientIdC.text, secretKeyC.text, '');
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF6366F1)),
|
style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF6366F1)),
|
||||||
child: const Text('ربط الآن', style: TextStyle(color: Colors.white)),
|
child: const Text('ربط الآن', style: TextStyle(color: Colors.white)),
|
||||||
|
|||||||
@@ -391,15 +391,12 @@ class DashboardView extends GetView<DashboardController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGamificationCard(Map gamification, bool isDark) {
|
Widget _buildGamificationCard(Map gamification, bool isDark) {
|
||||||
final points = gamification['points'] ?? 0;
|
final points = gamification['total_points'] ?? 0;
|
||||||
final level = gamification['level'] ?? 1;
|
final level = gamification['level'] ?? 1;
|
||||||
final levelName = gamification['level_name'] ?? 'مبتدئ';
|
final levelName = gamification['level_name'] ?? 'مبتدئ';
|
||||||
final currentLevelThreshold = gamification['current_level_threshold'] ?? 0;
|
final progressPercent = gamification['progress_percent'] ?? 0;
|
||||||
final nextLevelThreshold = gamification['next_level_threshold'] ?? 1000;
|
final badgesCount = gamification['badges_count'] ?? 0;
|
||||||
final progress = (points - currentLevelThreshold) /
|
final availableBadges = gamification['available_badges'] ?? 9;
|
||||||
((nextLevelThreshold - currentLevelThreshold) > 0
|
|
||||||
? (nextLevelThreshold - currentLevelThreshold)
|
|
||||||
: 1);
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@@ -447,15 +444,27 @@ class DashboardView extends GetView<DashboardController> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
InkWell(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
onTap: () => _showBadgesDialog(gamification),
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: Colors.white24,
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
borderRadius: BorderRadius.circular(20),
|
decoration: BoxDecoration(
|
||||||
),
|
color: Colors.white24,
|
||||||
child: const Text(
|
borderRadius: BorderRadius.circular(20),
|
||||||
'المكافآت',
|
),
|
||||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'المكافآت ',
|
||||||
|
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'$badgesCount/$availableBadges',
|
||||||
|
style: const TextStyle(color: Colors.white70, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -464,7 +473,7 @@ class DashboardView extends GetView<DashboardController> {
|
|||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: progress.clamp(0.0, 1.0).toDouble(),
|
value: progressPercent / 100.0,
|
||||||
backgroundColor: Colors.white24,
|
backgroundColor: Colors.white24,
|
||||||
color: Colors.amber,
|
color: Colors.amber,
|
||||||
minHeight: 8,
|
minHeight: 8,
|
||||||
@@ -472,7 +481,7 @@ class DashboardView extends GetView<DashboardController> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'باقي ${nextLevelThreshold - points} نقطة للمستوى القادم',
|
'باقي ${100 - progressPercent} نقطة للمستوى القادم',
|
||||||
style: const TextStyle(color: Colors.white70, fontSize: 12),
|
style: const TextStyle(color: Colors.white70, fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -480,6 +489,36 @@ class DashboardView extends GetView<DashboardController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showBadgesDialog(Map gamification) {
|
||||||
|
final badges = gamification['badges'] as List? ?? [];
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('شاراتك ومكافآتك', textAlign: TextAlign.center, style: TextStyle(color: Color(0xFF0F4C81))),
|
||||||
|
content: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: badges.isEmpty
|
||||||
|
? const Text('لم تحصل على أي شارات بعد. قم برفع الفواتير لتبدأ!', textAlign: TextAlign.center)
|
||||||
|
: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: badges.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final b = badges[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: Text(b['badge_icon'] ?? '🌟', style: const TextStyle(fontSize: 24)),
|
||||||
|
title: Text(b['badge_name'] ?? '', style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text('تم الحصول عليها: ${(b['earned_at'] ?? '').toString().split(' ')[0]}'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => Get.back(), child: const Text('إغلاق')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildQuotaMeter(Map subscription, bool isDark) {
|
Widget _buildQuotaMeter(Map subscription, bool isDark) {
|
||||||
int limit = subscription['limit'] ?? 100;
|
int limit = subscription['limit'] ?? 100;
|
||||||
int used = subscription['used'] ?? 0;
|
int used = subscription['used'] ?? 0;
|
||||||
|
|||||||
@@ -1572,7 +1572,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
<button x-show="subscription?.plan_id !== p.id" class="btn-primary" style="margin-top:auto;"
|
<button x-show="subscription?.plan_id !== p.id" class="btn-primary" style="margin-top:auto;"
|
||||||
@click="alert('يرجى التواصل مع الدعم الفني لترقية باقتك إلى ' + p.name_ar)">
|
@click="upgradePlan(p)">
|
||||||
ترقية الباقة الآن
|
ترقية الباقة الآن
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1868,11 +1868,7 @@
|
|||||||
<input type="password" x-model="connectData.secret_key" placeholder="أدخل Secret Key"
|
<input type="password" x-model="connectData.secret_key" placeholder="أدخل Secret Key"
|
||||||
class="form-input mono" required>
|
class="form-input mono" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">تسلسل مصدر الدخل <span class="form-label-sub">(مثال: 1)</span></label>
|
|
||||||
<input type="text" x-model="connectData.income_source_sequence" placeholder="1"
|
|
||||||
class="form-input mono">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-divider"></div>
|
<div class="modal-divider"></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@@ -2250,6 +2246,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ── UPLOAD PAYMENT RECEIPT MODAL ─────────────────────────── -->
|
||||||
|
<div x-show="showPaymentModal" x-cloak class="modal-backdrop">
|
||||||
|
<div class="modal-box">
|
||||||
|
<div class="modal-head">
|
||||||
|
<div class="modal-head-icon gold">💳</div>
|
||||||
|
<div style="flex:1;">
|
||||||
|
<div class="modal-title">تأكيد دفع الاشتراك</div>
|
||||||
|
<div class="modal-subtitle">يرجى تحويل المبلغ ثم رفع وصل الدفع</div>
|
||||||
|
</div>
|
||||||
|
<button @click="showPaymentModal = false" class="modal-close-btn">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-divider"></div>
|
||||||
|
<div class="modal-body" style="display:flex; flex-direction:column; gap:14px;">
|
||||||
|
<div style="background:var(--teal-subtle); border:1px solid rgba(4,120,87,0.2); border-radius:10px; padding:12px; text-align:center;">
|
||||||
|
<h4 style="color:var(--navy); font-weight:bold; margin-bottom:8px;">تفاصيل الدفع (CliQ)</h4>
|
||||||
|
<p style="margin-bottom:4px; font-size:14px;">يرجى تحويل مبلغ <strong style="color:var(--teal);" x-text="paymentData.amount + ' دينار'"></strong></p>
|
||||||
|
<p style="margin-bottom:8px; font-size:14px;">إلى حساب CliQ التالي:</p>
|
||||||
|
<div style="background:white; padding:8px; border-radius:6px; font-family:monospace; font-size:18px; font-weight:bold; color:var(--navy); display:inline-block; letter-spacing:2px;" x-text="paymentData.cliq_alias"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="margin-top:10px;">
|
||||||
|
<label class="form-label">صورة وصل التحويل</label>
|
||||||
|
<input type="file" @change="selectedFile = $event.target.files[0]" class="form-input" style="padding:8px;" accept="image/*,application/pdf" required>
|
||||||
|
<p style="font-size:12px; color:var(--text-3); margin-top:6px;">يرجى التأكد من وضوح رقم الحوالة والتاريخ.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-divider"></div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button @click="uploadReceipt" class="btn btn-navy" :disabled="isBusy" style="flex:1;">
|
||||||
|
<span x-show="!isBusy">📤 إرسال الوصل للتدقيق</span>
|
||||||
|
<span x-show="isBusy">⏳ جاري الإرسال...</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="showPaymentModal = false" class="btn btn-ghost">إلغاء</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ════════════════════════════════════════════════════════
|
<!-- ════════════════════════════════════════════════════════
|
||||||
ALPINE.JS — LOGIC UNCHANGED
|
ALPINE.JS — LOGIC UNCHANGED
|
||||||
@@ -2260,6 +2292,8 @@
|
|||||||
user: JSON.parse(localStorage.getItem('user')),
|
user: JSON.parse(localStorage.getItem('user')),
|
||||||
page: 'dashboard',
|
page: 'dashboard',
|
||||||
users: [], companies: [], invoices: [], tenants: [], subscription: null, plans: [],
|
users: [], companies: [], invoices: [], tenants: [], subscription: null, plans: [],
|
||||||
|
showPaymentModal: false,
|
||||||
|
paymentData: { cliq_alias: '', amount: 0, plan_name: '', request_id: '' },
|
||||||
stats: { total: 0, pending: 0, approved: 0 },
|
stats: { total: 0, pending: 0, approved: 0 },
|
||||||
|
|
||||||
showAddUserModal: false, showAddCompanyModal: false, showConnectModal: false,
|
showAddUserModal: false, showAddCompanyModal: false, showConnectModal: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user