Spaces:
Running
Running
Upload 36 files
#351
by
almortamoh
- opened
- .gitignore +142 -31
- ANDROID_STUDIO_GUIDE.md +200 -0
- APK_BUILD_GUIDE.md +206 -0
- APK_README.md +200 -0
- BUILD_APK_SIMPLE.bat +84 -0
- BUILD_FROM_GITHUB.bat +128 -0
- BUILD_INSTRUCTIONS.md +265 -0
- CLOUD_BUILD_QUICK.md +123 -0
- GITHUB_SETUP_GUIDE.md +209 -0
- INSTALL_ANDROID_SDK.bat +84 -0
- PROJECT_SUMMARY.md +219 -0
- QUICK_APK_BUILD.bat +79 -0
- QUICK_START.md +107 -0
- README.md +258 -21
- README_GITHUB.md +124 -0
- STEP_BY_STEP_APK.md +185 -0
- Windows +1 -0
- angular.json +147 -0
- app.js +780 -0
- auth.js +426 -0
- capacitor-simple.config.ts +49 -0
- capacitor.config.js +47 -0
- capacitor.config.ts +67 -0
- demo.html +333 -0
- index.html +369 -0
- ionic.config.json +8 -0
- notifications.js +371 -0
- package-lock.json +0 -0
- package-new.json +41 -0
- package-simple.json +42 -0
- package.json +79 -57
- styles.css +1334 -0
- tsconfig.app.json +13 -0
- tsconfig.json +27 -22
- wallets.js +430 -0
- الاخير.html +618 -0
.gitignore
CHANGED
@@ -1,41 +1,152 @@
|
|
1 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
#
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
.
|
8 |
-
!.yarn/patches
|
9 |
-
!.yarn/plugins
|
10 |
-
!.yarn/releases
|
11 |
-
!.yarn/versions
|
12 |
|
13 |
-
#
|
14 |
-
|
15 |
|
16 |
-
#
|
17 |
-
|
18 |
-
|
19 |
|
20 |
-
#
|
21 |
-
|
22 |
|
23 |
-
#
|
24 |
-
.
|
25 |
-
*.pem
|
26 |
|
27 |
-
#
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
.
|
|
|
|
|
|
|
32 |
|
33 |
-
#
|
34 |
-
|
|
|
35 |
|
36 |
-
#
|
37 |
-
|
38 |
|
39 |
-
#
|
40 |
*.tsbuildinfo
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
lerna-debug.log*
|
8 |
|
9 |
+
# Runtime data
|
10 |
+
pids
|
11 |
+
*.pid
|
12 |
+
*.seed
|
13 |
+
*.pid.lock
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
16 |
+
lib-cov
|
17 |
|
18 |
+
# Coverage directory used by tools like istanbul
|
19 |
+
coverage
|
20 |
+
*.lcov
|
21 |
|
22 |
+
# nyc test coverage
|
23 |
+
.nyc_output
|
24 |
|
25 |
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
26 |
+
.grunt
|
|
|
27 |
|
28 |
+
# Bower dependency directory (https://bower.io/)
|
29 |
+
bower_components
|
30 |
+
|
31 |
+
# node-waf configuration
|
32 |
+
.lock-wscript
|
33 |
+
|
34 |
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
35 |
+
build/Release
|
36 |
|
37 |
+
# Dependency directories
|
38 |
+
node_modules/
|
39 |
+
jspm_packages/
|
40 |
|
41 |
+
# TypeScript v1 declaration files
|
42 |
+
typings/
|
43 |
|
44 |
+
# TypeScript cache
|
45 |
*.tsbuildinfo
|
46 |
+
|
47 |
+
# Optional npm cache directory
|
48 |
+
.npm
|
49 |
+
|
50 |
+
# Optional eslint cache
|
51 |
+
.eslintcache
|
52 |
+
|
53 |
+
# Microbundle cache
|
54 |
+
.rpt2_cache/
|
55 |
+
.rts2_cache_cjs/
|
56 |
+
.rts2_cache_es/
|
57 |
+
.rts2_cache_umd/
|
58 |
+
|
59 |
+
# Optional REPL history
|
60 |
+
.node_repl_history
|
61 |
+
|
62 |
+
# Output of 'npm pack'
|
63 |
+
*.tgz
|
64 |
+
|
65 |
+
# Yarn Integrity file
|
66 |
+
.yarn-integrity
|
67 |
+
|
68 |
+
# dotenv environment variables file
|
69 |
+
.env
|
70 |
+
.env.test
|
71 |
+
|
72 |
+
# parcel-bundler cache (https://parceljs.org/)
|
73 |
+
.cache
|
74 |
+
.parcel-cache
|
75 |
+
|
76 |
+
# Next.js build output
|
77 |
+
.next
|
78 |
+
|
79 |
+
# Nuxt.js build / generate output
|
80 |
+
.nuxt
|
81 |
+
dist
|
82 |
+
|
83 |
+
# Gatsby files
|
84 |
+
.cache/
|
85 |
+
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
86 |
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
87 |
+
# public
|
88 |
+
|
89 |
+
# vuepress build output
|
90 |
+
.vuepress/dist
|
91 |
+
|
92 |
+
# Serverless directories
|
93 |
+
.serverless/
|
94 |
+
|
95 |
+
# FuseBox cache
|
96 |
+
.fusebox/
|
97 |
+
|
98 |
+
# DynamoDB Local files
|
99 |
+
.dynamodb/
|
100 |
+
|
101 |
+
# TernJS port file
|
102 |
+
.tern-port
|
103 |
+
|
104 |
+
# Capacitor
|
105 |
+
.capacitor/
|
106 |
+
capacitor.config.json
|
107 |
+
|
108 |
+
# Android
|
109 |
+
android/app/build/
|
110 |
+
android/build/
|
111 |
+
android/.gradle/
|
112 |
+
android/local.properties
|
113 |
+
android/app/release/
|
114 |
+
android/app/debug/
|
115 |
+
android/gradle/
|
116 |
+
android/gradlew
|
117 |
+
android/gradlew.bat
|
118 |
+
|
119 |
+
# iOS
|
120 |
+
ios/build/
|
121 |
+
ios/App/Pods/
|
122 |
+
ios/App/App.xcworkspace/xcuserdata/
|
123 |
+
ios/App/App.xcodeproj/xcuserdata/
|
124 |
+
ios/App/App.xcodeproj/project.xcworkspace/xcuserdata/
|
125 |
+
|
126 |
+
# Editor directories and files
|
127 |
+
.vscode/
|
128 |
+
.idea/
|
129 |
+
*.swp
|
130 |
+
*.swo
|
131 |
+
*~
|
132 |
+
|
133 |
+
# OS generated files
|
134 |
+
.DS_Store
|
135 |
+
.DS_Store?
|
136 |
+
._*
|
137 |
+
.Spotlight-V100
|
138 |
+
.Trashes
|
139 |
+
ehthumbs.db
|
140 |
+
Thumbs.db
|
141 |
+
|
142 |
+
# Local development
|
143 |
+
*.local
|
144 |
+
|
145 |
+
# Build outputs
|
146 |
+
build/
|
147 |
+
dist/
|
148 |
+
www/build/
|
149 |
+
|
150 |
+
# Temporary files
|
151 |
+
tmp/
|
152 |
+
temp/
|
ANDROID_STUDIO_GUIDE.md
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📱 دليل بناء APK باستخدام Android Studio
|
2 |
+
|
3 |
+
## 🎯 **الهدف:** تحويل المشروع إلى ملف APK قابل للتثبيت
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## 📋 **الخطوات المفصلة:**
|
8 |
+
|
9 |
+
### **الخطوة 1: فتح المشروع** ⏱️ (2-3 دقائق)
|
10 |
+
|
11 |
+
1. **افتح Android Studio**
|
12 |
+
2. **اختر "Open an Existing Project"** أو **"Open"**
|
13 |
+
3. **انتقل إلى مجلد المشروع:**
|
14 |
+
```
|
15 |
+
E:\almada\android
|
16 |
+
```
|
17 |
+
4. **اختر مجلد `android`** (وليس المجلد الرئيسي)
|
18 |
+
5. **اضغط "OK"**
|
19 |
+
|
20 |
+
### **الخطوة 2: انتظار المزامنة** ⏱️ (5-10 دقائق)
|
21 |
+
|
22 |
+
عند فتح المشروع لأول مرة، سيقوم Android Studio بـ:
|
23 |
+
- 📥 **تحميل Gradle** (إذا لم يكن مثبت)
|
24 |
+
- 📦 **تحميل التبعيات** (Dependencies)
|
25 |
+
- 🔄 **مزامنة المشروع** (Sync)
|
26 |
+
|
27 |
+
**انتظر حتى تكتمل العملية!** ستظهر رسالة في الأسفل:
|
28 |
+
```
|
29 |
+
✅ Gradle sync finished
|
30 |
+
```
|
31 |
+
|
32 |
+
### **الخطوة 3: حل المشاكل المحتملة** ⏱️ (2-5 دقائق)
|
33 |
+
|
34 |
+
إذا ظهرت أي رسائل خطأ:
|
35 |
+
|
36 |
+
#### **مشكلة SDK:**
|
37 |
+
```
|
38 |
+
SDK location not found
|
39 |
+
```
|
40 |
+
**الحل:**
|
41 |
+
- اذهب إلى **File > Project Structure**
|
42 |
+
- اختر **SDK Location**
|
43 |
+
- تأكد من مسار Android SDK
|
44 |
+
|
45 |
+
#### **مشكلة Gradle:**
|
46 |
+
```
|
47 |
+
Gradle version not supported
|
48 |
+
```
|
49 |
+
**الحل:**
|
50 |
+
- اضغط على **"Update Gradle"** في الرسالة
|
51 |
+
- أو اذهب إلى **File > Project Structure > Project**
|
52 |
+
|
53 |
+
### **الخطوة 4: بناء APK** ⏱️ (3-5 دقائق)
|
54 |
+
|
55 |
+
1. **في شريط القوائم، اختر:**
|
56 |
+
```
|
57 |
+
Build > Build Bundle(s) / APK(s) > Build APK(s)
|
58 |
+
```
|
59 |
+
|
60 |
+
2. **انتظر اكتمال البناء** - ستظهر رسالة في الأسفل:
|
61 |
+
```
|
62 |
+
⏳ Building APK...
|
63 |
+
✅ APK(s) generated successfully
|
64 |
+
```
|
65 |
+
|
66 |
+
3. **عند اكتمال البناء، ستظهر نافذة:**
|
67 |
+
```
|
68 |
+
APK(s) generated successfully.
|
69 |
+
|
70 |
+
[locate] [analyze]
|
71 |
+
```
|
72 |
+
|
73 |
+
4. **اضغط "locate"** للذهاب إلى مجلد APK
|
74 |
+
|
75 |
+
### **الخطوة 5: العثور على ملف APK** ⏱️ (1 دقيقة)
|
76 |
+
|
77 |
+
ملف APK سيكون في:
|
78 |
+
```
|
79 |
+
E:\almada\android\app\build\outputs\apk\debug\app-debug.apk
|
80 |
+
```
|
81 |
+
|
82 |
+
**معلومات الملف:**
|
83 |
+
- **الاسم:** `app-debug.apk`
|
84 |
+
- **الحجم:** ~15-20 MB
|
85 |
+
- **النوع:** Debug APK (للاختبار)
|
86 |
+
|
87 |
+
---
|
88 |
+
|
89 |
+
## 🔧 **استكشاف الأخطاء:**
|
90 |
+
|
91 |
+
### **خطأ: "SDK not found"**
|
92 |
+
```bash
|
93 |
+
# الحل:
|
94 |
+
1. اذهب إلى File > Settings
|
95 |
+
2. اختر Appearance & Behavior > System Settings > Android SDK
|
96 |
+
3. تأكد من تثبيت Android SDK
|
97 |
+
```
|
98 |
+
|
99 |
+
### **خطأ: "Gradle sync failed"**
|
100 |
+
```bash
|
101 |
+
# الحل:
|
102 |
+
1. اضغط "Try Again"
|
103 |
+
2. أو اذهب إلى File > Sync Project with Gradle Files
|
104 |
+
```
|
105 |
+
|
106 |
+
### **خطأ: "Build failed"**
|
107 |
+
```bash
|
108 |
+
# الحل:
|
109 |
+
1. اذهب إلى Build > Clean Project
|
110 |
+
2. ثم Build > Rebuild Project
|
111 |
+
```
|
112 |
+
|
113 |
+
---
|
114 |
+
|
115 |
+
## 📱 **اختبار التطبيق:**
|
116 |
+
|
117 |
+
### **الطريقة 1: على الكمبيوتر (محاكي)**
|
118 |
+
1. **إنشاء محاكي:**
|
119 |
+
- اذهب إلى **Tools > AVD Manager**
|
120 |
+
- اضغط **"Create Virtual Device"**
|
121 |
+
- اختر جهاز (مثل Pixel 4)
|
122 |
+
- اختر نظام Android (API 30+)
|
123 |
+
|
124 |
+
2. **تشغيل التطبيق:**
|
125 |
+
- اضغط **Run** (الزر الأخضر)
|
126 |
+
- اختر المحاكي
|
127 |
+
- انتظر تشغيل التطبيق
|
128 |
+
|
129 |
+
### **الطريقة 2: على الهاتف الحقيقي**
|
130 |
+
1. **تفعيل Developer Options:**
|
131 |
+
- اذهب إلى **Settings > About Phone**
|
132 |
+
- اضغط على **Build Number** 7 مرات
|
133 |
+
- ارجع إلى Settings وادخل **Developer Options**
|
134 |
+
- فعل **USB Debugging**
|
135 |
+
|
136 |
+
2. **توصيل الهاتف:**
|
137 |
+
- وصل الهاتف بـ USB
|
138 |
+
- اختر **File Transfer** في الهاتف
|
139 |
+
- في Android Studio، اختر جهازك من القائمة
|
140 |
+
- اضغط **Run**
|
141 |
+
|
142 |
+
### **الطريقة 3: تثبيت APK يدوياً**
|
143 |
+
1. **نسخ APK إلى الهاتف**
|
144 |
+
2. **في الهاتف:**
|
145 |
+
- اذهب إلى **Settings > Security**
|
146 |
+
- فعل **"Install from Unknown Sources"**
|
147 |
+
- افتح ملف APK واضغط **Install**
|
148 |
+
|
149 |
+
---
|
150 |
+
|
151 |
+
## 🎯 **بيانات التجربة:**
|
152 |
+
|
153 |
+
بعد تثبيت التطبيق، استخدم:
|
154 |
+
- **رقم الهاتف:** `777123456`
|
155 |
+
- **رمز PIN:** `1234`
|
156 |
+
|
157 |
+
---
|
158 |
+
|
159 |
+
## 📊 **معلومات التطبيق:**
|
160 |
+
|
161 |
+
| المعلومة | القيمة |
|
162 |
+
|---------|--------|
|
163 |
+
| **اسم التطبيق** | محفظتي الموحدة |
|
164 |
+
| **Package Name** | com.almada.unifiedwallet |
|
165 |
+
| **الإصدار** | 1.0.0 |
|
166 |
+
| **حجم APK** | ~15-20 MB |
|
167 |
+
| **الحد الأدنى** | Android 7.0 (API 24) |
|
168 |
+
|
169 |
+
---
|
170 |
+
|
171 |
+
## 🎉 **النجاح!**
|
172 |
+
|
173 |
+
عند اكتمال جميع الخطوات، ستحصل على:
|
174 |
+
- ✅ **ملف APK** جاهز للتثبيت
|
175 |
+
- ✅ **تطبيق يعمل** على الأندرويد
|
176 |
+
- ✅ **واجهة عربية** كاملة
|
177 |
+
- ✅ **جميع الميزات** متاحة
|
178 |
+
|
179 |
+
---
|
180 |
+
|
181 |
+
## 💡 **نصائح مهمة:**
|
182 |
+
|
183 |
+
### **ل��بناء الناجح:**
|
184 |
+
- تأكد من **اتصال الإنترنت** أثناء أول مزامنة
|
185 |
+
- **لا تغلق** Android Studio أثناء التحميل
|
186 |
+
- **انتظر** اكتمال جميع العمليات
|
187 |
+
|
188 |
+
### **للاختبار:**
|
189 |
+
- اختبر على **أجهزة مختلفة** إن أمكن
|
190 |
+
- تأكد من **جميع الميزات** تعمل
|
191 |
+
- اختبر **تسجيل الدخول** والتنقل
|
192 |
+
|
193 |
+
### **للمشاكل:**
|
194 |
+
- راجع **Build Output** في الأسفل
|
195 |
+
- استخدم **"Clean Project"** عند المشاكل
|
196 |
+
- أعد تشغيل **Android Studio** إذا لزم الأمر
|
197 |
+
|
198 |
+
---
|
199 |
+
|
200 |
+
**🚀 مبروك! تطبيقك جاهز للعالم!**
|
APK_BUILD_GUIDE.md
ADDED
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# دليل بناء APK - محفظتي الموحدة
|
2 |
+
|
3 |
+
## 🎯 **الوضع الحالي**
|
4 |
+
|
5 |
+
✅ **تم إنجازه:**
|
6 |
+
- ✅ إعداد مشروع Capacitor كامل
|
7 |
+
- ✅ تثبيت Java JDK 11
|
8 |
+
- ✅ إضافة منصة الأندرويد
|
9 |
+
- ✅ نسخ ملفات التطبيق إلى مجلد www
|
10 |
+
- ✅ إضافة الأذونات المطلوبة
|
11 |
+
- ✅ إعداد ملفات التكوين
|
12 |
+
|
13 |
+
❌ **المطلوب لإكمال البناء:**
|
14 |
+
- ❌ تثبيت Android SDK
|
15 |
+
- ❌ إعداد متغيرات البيئة
|
16 |
+
- ❌ بناء APK
|
17 |
+
|
18 |
+
---
|
19 |
+
|
20 |
+
## 📋 **خطوات إكمال بناء APK**
|
21 |
+
|
22 |
+
### **الطريقة 1: استخدام Android Studio (الأسهل)**
|
23 |
+
|
24 |
+
#### 1. تحميل وتثبيت Android Studio:
|
25 |
+
```
|
26 |
+
https://developer.android.com/studio
|
27 |
+
```
|
28 |
+
|
29 |
+
#### 2. فتح المشروع:
|
30 |
+
```bash
|
31 |
+
cd E:\almada\android
|
32 |
+
# ثم فتح المجلد في Android Studio
|
33 |
+
```
|
34 |
+
|
35 |
+
#### 3. بناء APK:
|
36 |
+
- في Android Studio: **Build > Build Bundle(s) / APK(s) > Build APK(s)**
|
37 |
+
- انتظار اكتمال البناء
|
38 |
+
- ستجد APK في: `android/app/build/outputs/apk/debug/`
|
39 |
+
|
40 |
+
---
|
41 |
+
|
42 |
+
### **الطريقة 2: سطر الأوامر (متقدم)**
|
43 |
+
|
44 |
+
#### 1. تثبيت Android SDK:
|
45 |
+
```bash
|
46 |
+
# تحميل Command Line Tools من:
|
47 |
+
# https://developer.android.com/studio#command-tools
|
48 |
+
|
49 |
+
# استخراج إلى مجلد مثل:
|
50 |
+
# C:\Android\cmdline-tools\latest\
|
51 |
+
```
|
52 |
+
|
53 |
+
#### 2. إعداد متغيرات البيئة:
|
54 |
+
```bash
|
55 |
+
# إضافة إلى متغيرات البيئة:
|
56 |
+
ANDROID_HOME=C:\Android
|
57 |
+
ANDROID_SDK_ROOT=C:\Android
|
58 |
+
PATH=%PATH%;%ANDROID_HOME%\cmdline-tools\latest\bin
|
59 |
+
PATH=%PATH%;%ANDROID_HOME%\platform-tools
|
60 |
+
```
|
61 |
+
|
62 |
+
#### 3. تثبيت SDK Components:
|
63 |
+
```bash
|
64 |
+
sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.0"
|
65 |
+
```
|
66 |
+
|
67 |
+
#### 4. بناء APK:
|
68 |
+
```bash
|
69 |
+
cd E:\almada\android
|
70 |
+
.\gradlew assembleDebug
|
71 |
+
```
|
72 |
+
|
73 |
+
---
|
74 |
+
|
75 |
+
### **الطريقة 3: استخدام Ionic CLI (الأبسط)**
|
76 |
+
|
77 |
+
#### 1. تثبيت Android Studio أولاً (للحصول على SDK)
|
78 |
+
|
79 |
+
#### 2. استخدام Ionic:
|
80 |
+
```bash
|
81 |
+
cd E:\almada
|
82 |
+
ionic cap build android
|
83 |
+
```
|
84 |
+
|
85 |
+
---
|
86 |
+
|
87 |
+
## 📱 **ملفات APK المتوقعة**
|
88 |
+
|
89 |
+
بعد البناء الناجح ستجد:
|
90 |
+
|
91 |
+
### **APK للتطوير:**
|
92 |
+
```
|
93 |
+
android/app/build/outputs/apk/debug/app-debug.apk
|
94 |
+
```
|
95 |
+
|
96 |
+
### **APK للإنتاج:**
|
97 |
+
```
|
98 |
+
android/app/build/outputs/apk/release/app-release.apk
|
99 |
+
```
|
100 |
+
|
101 |
+
---
|
102 |
+
|
103 |
+
## 🔧 **إعدادات إضافية للتطبيق**
|
104 |
+
|
105 |
+
### **الأذونات المضافة:**
|
106 |
+
```xml
|
107 |
+
<!-- الأذونات الأساسية -->
|
108 |
+
<uses-permission android:name="android.permission.INTERNET" />
|
109 |
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
110 |
+
<uses-permission android:name="android.permission.CAMERA" />
|
111 |
+
<uses-permission android:name="android.permission.VIBRATE" />
|
112 |
+
|
113 |
+
<!-- أذونات SMS (للمستقبل) -->
|
114 |
+
<uses-permission android:name="android.permission.READ_SMS" />
|
115 |
+
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
116 |
+
|
117 |
+
<!-- أذونات البصمة -->
|
118 |
+
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
119 |
+
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
120 |
+
```
|
121 |
+
|
122 |
+
### **معلومات التطبيق:**
|
123 |
+
- **اسم التطبيق:** محفظتي الموحدة
|
124 |
+
- **Package ID:** com.almada.unifiedwallet
|
125 |
+
- **الإصدار:** 1.0.0
|
126 |
+
|
127 |
+
---
|
128 |
+
|
129 |
+
## 🚀 **اختبار التطبيق**
|
130 |
+
|
131 |
+
### **تثبيت APK على الهاتف:**
|
132 |
+
```bash
|
133 |
+
# تفعيل Developer Options و USB Debugging
|
134 |
+
# ثم:
|
135 |
+
adb install app-debug.apk
|
136 |
+
```
|
137 |
+
|
138 |
+
### **أو نسخ APK إلى الهاتف وتثبيته يدوياً**
|
139 |
+
|
140 |
+
---
|
141 |
+
|
142 |
+
## 📊 **الميزات المتاحة في التطبيق**
|
143 |
+
|
144 |
+
### **الصفحة الرئيسية:**
|
145 |
+
- تسجيل دخول برقم هاتف + PIN
|
146 |
+
- عرض المحافظ الـ6 (جوالي، ONE Cash، إلخ)
|
147 |
+
- عرض الأرصدة الموحدة
|
148 |
+
|
149 |
+
### **الميزات المتقدمة:**
|
150 |
+
- واجهة عربية كاملة (RTL)
|
151 |
+
- تصميم متجاوب
|
152 |
+
- رسوم متحركة سلسة
|
153 |
+
- دعم الوضع الليلي
|
154 |
+
|
155 |
+
### **الأمان:**
|
156 |
+
- تشفير البيانات محلياً
|
157 |
+
- حماية PIN
|
158 |
+
- جلسات آمنة
|
159 |
+
|
160 |
+
---
|
161 |
+
|
162 |
+
## 🎯 **الخطوات التالية بعد البناء**
|
163 |
+
|
164 |
+
### **للاختبار:**
|
165 |
+
1. تثبيت APK على الهاتف
|
166 |
+
2. اختبار تسجيل الدخول (777123456 / 1234)
|
167 |
+
3. اختبار جميع الميزات
|
168 |
+
4. التأكد من الأداء
|
169 |
+
|
170 |
+
### **للتطوير:**
|
171 |
+
1. إضافة ميزات قراءة SMS
|
172 |
+
2. تطوير نظام الإشعارات
|
173 |
+
3. إضافة المزيد من المحافظ
|
174 |
+
4. تحسين الأمان
|
175 |
+
|
176 |
+
### **للنشر:**
|
177 |
+
1. إنشاء حساب Google Play Developer
|
178 |
+
2. إعداد التوقيع للإنتاج
|
179 |
+
3. رفع التطبيق للمراجعة
|
180 |
+
4. التسويق والترويج
|
181 |
+
|
182 |
+
---
|
183 |
+
|
184 |
+
## 💡 **نصائح مهمة**
|
185 |
+
|
186 |
+
### **للبناء الناجح:**
|
187 |
+
- تأكد من تثبيت Java JDK 11+ ✅
|
188 |
+
- تأكد من تثبيت Android SDK
|
189 |
+
- تأكد من إعداد متغيرات البيئة
|
190 |
+
- استخدم Android Studio للسهولة
|
191 |
+
|
192 |
+
### **للاختبار:**
|
193 |
+
- اختبر على أجهزة مختلفة
|
194 |
+
- اختبر جميع الميزات
|
195 |
+
- تأكد من الأداء والاستقرار
|
196 |
+
|
197 |
+
### **للأمان:**
|
198 |
+
- لا تشارك ملفات التوقيع
|
199 |
+
- استخدم ProGuard للحماية
|
200 |
+
- اختبر الأمان بعناية
|
201 |
+
|
202 |
+
---
|
203 |
+
|
204 |
+
**🎉 مبروك! تطبيقك جاهز للبناء والاختبار!**
|
205 |
+
|
206 |
+
للمساعدة في أي خطوة، راجع الوثائق أو تواصل مع فريق التطوير.
|
APK_README.md
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📱 محفظتي الموحدة - تطبيق APK جاهز!
|
2 |
+
|
3 |
+
## 🎉 **تهانينا! تطبيقك جاهز للبناء**
|
4 |
+
|
5 |
+
تم إعداد مشروع **محفظتي الموحدة** بنجاح كتطبيق أندرويد باستخدام تقنية **Capacitor**.
|
6 |
+
|
7 |
+
---
|
8 |
+
|
9 |
+
## 🚀 **طرق بناء APK**
|
10 |
+
|
11 |
+
### **الطريقة الأسهل: Android Studio**
|
12 |
+
1. حمل وثبت [Android Studio](https://developer.android.com/studio)
|
13 |
+
2. افتح مجلد `android` في Android Studio
|
14 |
+
3. اختر **Build > Build APK**
|
15 |
+
4. انتظر اكتمال البناء
|
16 |
+
5. ستجد APK في: `android/app/build/outputs/apk/debug/`
|
17 |
+
|
18 |
+
### **الطريقة السريعة: ملف BAT**
|
19 |
+
1. شغل `INSTALL_ANDROID_SDK.bat` (مرة واحدة فقط)
|
20 |
+
2. اتبع التعليمات لتثبيت Android SDK
|
21 |
+
3. شغل `QUICK_APK_BUILD.bat`
|
22 |
+
4. انتظر اكتمال البناء
|
23 |
+
|
24 |
+
### **الطريقة اليدوية: سطر الأوامر**
|
25 |
+
```bash
|
26 |
+
# تأكد من تثبيت Java و Android SDK
|
27 |
+
java -version
|
28 |
+
sdkmanager --version
|
29 |
+
|
30 |
+
# بناء APK
|
31 |
+
cd android
|
32 |
+
gradlew assembleDebug
|
33 |
+
```
|
34 |
+
|
35 |
+
---
|
36 |
+
|
37 |
+
## 📋 **متطلبات البناء**
|
38 |
+
|
39 |
+
### **مثبت بالفعل:**
|
40 |
+
✅ **Node.js** و **npm**
|
41 |
+
✅ **Capacitor** و **التبعيات**
|
42 |
+
✅ **Java JDK 11**
|
43 |
+
✅ **مشروع Android** جاهز
|
44 |
+
|
45 |
+
### **مطلوب تثبيته:**
|
46 |
+
❌ **Android SDK** (عبر Android Studio أو Command Line Tools)
|
47 |
+
|
48 |
+
---
|
49 |
+
|
50 |
+
## 📱 **معلومات التطبيق**
|
51 |
+
|
52 |
+
| المعلومة | القيمة |
|
53 |
+
|---------|--------|
|
54 |
+
| **اسم التطبيق** | محفظتي الموحدة |
|
55 |
+
| **Package ID** | com.almada.unifiedwallet |
|
56 |
+
| **الإصدار** | 1.0.0 |
|
57 |
+
| **الحد الأدنى للأندرويد** | API 24 (Android 7.0) |
|
58 |
+
|
59 |
+
---
|
60 |
+
|
61 |
+
## 🔑 **بيانات التجربة**
|
62 |
+
|
63 |
+
للدخول إلى التطبيق بعد التثبيت:
|
64 |
+
- **رقم الهاتف:** `777123456`
|
65 |
+
- **رمز PIN:** `1234`
|
66 |
+
|
67 |
+
---
|
68 |
+
|
69 |
+
## 🎯 **الميزات المتاحة**
|
70 |
+
|
71 |
+
### **الأساسية:**
|
72 |
+
- ✅ تسجيل دخول آمن برقم الهاتف + PIN
|
73 |
+
- ✅ عرض 6 محافظ يمنية (جوالي، ONE Cash، إلخ)
|
74 |
+
- ✅ واجهة عربية كاملة (RTL)
|
75 |
+
- ✅ تصميم متجاوب وجذاب
|
76 |
+
|
77 |
+
### **المتقدمة:**
|
78 |
+
- ✅ رسوم متحركة سلسة
|
79 |
+
- ✅ دعم الوضع الليلي
|
80 |
+
- ✅ حفظ البيانات محلياً
|
81 |
+
- ✅ أمان متعدد الطبقات
|
82 |
+
|
83 |
+
### **المستقبلية (جاهزة للتطوير):**
|
84 |
+
- 🔄 قراءة رسائل SMS تلقائياً
|
85 |
+
- 🔄 مصادقة بيومترية (بصمة/وجه)
|
86 |
+
- 🔄 إشعارات ذكية
|
87 |
+
- 🔄 تحليل الإنفاق
|
88 |
+
|
89 |
+
---
|
90 |
+
|
91 |
+
## 📂 **هيكل الملفات**
|
92 |
+
|
93 |
+
```
|
94 |
+
almada/
|
95 |
+
├── 📱 android/ # مشروع الأندرويد
|
96 |
+
├── 🌐 www/ # ملفات التطبيق
|
97 |
+
├── 📄 capacitor.config.ts # إعدادات Capacitor
|
98 |
+
├── 📦 package.json # تبعيات المشروع
|
99 |
+
├── 🔨 QUICK_APK_BUILD.bat # بناء سريع
|
100 |
+
├── ⚙️ INSTALL_ANDROID_SDK.bat # تثبيت SDK
|
101 |
+
└── 📖 APK_BUILD_GUIDE.md # دليل مفصل
|
102 |
+
```
|
103 |
+
|
104 |
+
---
|
105 |
+
|
106 |
+
## 🛠️ **استكشاف الأخطاء**
|
107 |
+
|
108 |
+
### **خطأ: Java غير موجود**
|
109 |
+
```bash
|
110 |
+
# تحقق من تثبيت Java
|
111 |
+
java -version
|
112 |
+
|
113 |
+
# إذا لم يكن مثبت، حمل من:
|
114 |
+
# https://adoptium.net/
|
115 |
+
```
|
116 |
+
|
117 |
+
### **خطأ: Android SDK غير موجود**
|
118 |
+
```bash
|
119 |
+
# شغل ملف التثبيت
|
120 |
+
INSTALL_ANDROID_SDK.bat
|
121 |
+
|
122 |
+
# أو ثبت Android Studio
|
123 |
+
```
|
124 |
+
|
125 |
+
### **خطأ: Gradle Build فشل**
|
126 |
+
```bash
|
127 |
+
# نظف المشروع
|
128 |
+
cd android
|
129 |
+
gradlew clean
|
130 |
+
|
131 |
+
# أعد البناء
|
132 |
+
gradlew assembleDebug
|
133 |
+
```
|
134 |
+
|
135 |
+
---
|
136 |
+
|
137 |
+
## 📲 **تثبيت APK على الهاتف**
|
138 |
+
|
139 |
+
### **الطريقة 1: USB**
|
140 |
+
```bash
|
141 |
+
# فعل USB Debugging في الهاتف
|
142 |
+
# ثم:
|
143 |
+
adb install app-debug.apk
|
144 |
+
```
|
145 |
+
|
146 |
+
### **الطريقة 2: يدوياً**
|
147 |
+
1. انسخ ملف APK إلى الهاتف
|
148 |
+
2. فعل "مصادر غير معروفة" في الإعدادات
|
149 |
+
3. اضغط على ملف APK لتثبيته
|
150 |
+
|
151 |
+
---
|
152 |
+
|
153 |
+
## 🎯 **الخطوات التالية**
|
154 |
+
|
155 |
+
### **للاختبار:**
|
156 |
+
1. 📱 ثبت APK على الهاتف
|
157 |
+
2. 🔐 جرب تسجيل الدخول
|
158 |
+
3. 💳 استكشف المحافظ
|
159 |
+
4. 🎨 جرب الميزات المختلفة
|
160 |
+
|
161 |
+
### **للتطوير:**
|
162 |
+
1. 📨 أضف قراءة SMS
|
163 |
+
2. 🔔 طور الإشعارات
|
164 |
+
3. 📊 أضف تحليل البيانات
|
165 |
+
4. 🛡️ حسن الأمان
|
166 |
+
|
167 |
+
### **للنشر:**
|
168 |
+
1. 🏪 أنشئ حساب Google Play
|
169 |
+
2. 🔏 أعد التوقيع للإنتاج
|
170 |
+
3. 📤 ارفع للمراجعة
|
171 |
+
4. 📢 سوق التطبيق
|
172 |
+
|
173 |
+
---
|
174 |
+
|
175 |
+
## 💡 **نصائح مهمة**
|
176 |
+
|
177 |
+
### **للبناء الناجح:**
|
178 |
+
- استخدم **Android Studio** للسهولة
|
179 |
+
- تأكد من **اتصال الإنترنت** أثناء البناء
|
180 |
+
- **أعد تشغيل** Command Prompt بعد تثبيت SDK
|
181 |
+
|
182 |
+
### **للاختبار:**
|
183 |
+
- اختبر على **أجهزة مختلفة**
|
184 |
+
- تأكد من **جميع الميزات**
|
185 |
+
- راقب **الأداء والاستقرار**
|
186 |
+
|
187 |
+
---
|
188 |
+
|
189 |
+
## 📞 **الدعم والمساعدة**
|
190 |
+
|
191 |
+
للحصول على المساعدة:
|
192 |
+
1. راجع `APK_BUILD_GUIDE.md` للتفاصيل
|
193 |
+
2. تحقق من `BUILD_INSTRUCTIONS.md` للتعليمات الكاملة
|
194 |
+
3. تواصل مع فريق التطوير
|
195 |
+
|
196 |
+
---
|
197 |
+
|
198 |
+
**🎊 مبروك! تطبيقك جاهز للعالم!**
|
199 |
+
|
200 |
+
*تطبيق محفظتي الموحدة - المدى للخدمات البرمجية التسويقية والإعلانية*
|
BUILD_APK_SIMPLE.bat
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
echo ========================================
|
3 |
+
echo محفظتي الموحدة - بناء APK مبسط
|
4 |
+
echo ========================================
|
5 |
+
echo.
|
6 |
+
|
7 |
+
echo 🔍 التحقق من المتطلبات...
|
8 |
+
|
9 |
+
:: التحقق من Java
|
10 |
+
java -version >nul 2>&1
|
11 |
+
if %errorlevel% neq 0 (
|
12 |
+
echo ❌ Java غير مثبت!
|
13 |
+
echo يرجى تثبيت Java JDK من: https://adoptium.net/
|
14 |
+
pause
|
15 |
+
exit /b 1
|
16 |
+
)
|
17 |
+
echo ✅ Java متوفر
|
18 |
+
|
19 |
+
:: التحقق من Node.js
|
20 |
+
node --version >nul 2>&1
|
21 |
+
if %errorlevel% neq 0 (
|
22 |
+
echo ❌ Node.js غير مثبت!
|
23 |
+
echo يرجى تثبيت Node.js من: https://nodejs.org/
|
24 |
+
pause
|
25 |
+
exit /b 1
|
26 |
+
)
|
27 |
+
echo ✅ Node.js متوفر
|
28 |
+
|
29 |
+
echo.
|
30 |
+
echo 📦 تثبيت أدوات البناء...
|
31 |
+
call npm install -g @ionic/cli @capacitor/cli
|
32 |
+
|
33 |
+
echo.
|
34 |
+
echo 🔄 مزامنة المشروع...
|
35 |
+
call cap sync android
|
36 |
+
|
37 |
+
echo.
|
38 |
+
echo 🏗️ بناء APK...
|
39 |
+
echo هذه العملية قد تستغرق 5-10 دقائق...
|
40 |
+
echo يرجى الانتظار...
|
41 |
+
|
42 |
+
cd android
|
43 |
+
call gradlew assembleDebug
|
44 |
+
|
45 |
+
if %errorlevel% equ 0 (
|
46 |
+
echo.
|
47 |
+
echo ========================================
|
48 |
+
echo 🎉 تم بناء APK بنجاح!
|
49 |
+
echo ========================================
|
50 |
+
echo.
|
51 |
+
echo 📱 ملف APK متوفر في:
|
52 |
+
echo android\app\build\outputs\apk\debug\app-debug.apk
|
53 |
+
echo.
|
54 |
+
echo 📋 معلومات التطبيق:
|
55 |
+
echo - الاسم: محفظتي الموحدة
|
56 |
+
echo - الحجم: ~15-20 MB
|
57 |
+
echo - النوع: Debug APK
|
58 |
+
echo.
|
59 |
+
echo 🔑 بيانات التجربة:
|
60 |
+
echo - رقم الهاتف: 777123456
|
61 |
+
echo - رمز PIN: 1234
|
62 |
+
echo.
|
63 |
+
echo 📲 لتثبيت التطبيق:
|
64 |
+
echo 1. انسخ ملف APK إلى هاتفك
|
65 |
+
echo 2. فعل "مصادر غير معروفة" في إعدادات الأمان
|
66 |
+
echo 3. اضغط على ملف APK لتثبيته
|
67 |
+
echo.
|
68 |
+
|
69 |
+
:: فتح مجلد APK
|
70 |
+
explorer "app\build\outputs\apk\debug\"
|
71 |
+
|
72 |
+
) else (
|
73 |
+
echo.
|
74 |
+
echo ❌ فشل في بناء APK!
|
75 |
+
echo.
|
76 |
+
echo 🔧 الحلول المقترحة:
|
77 |
+
echo 1. تأكد من تثبيت Android SDK
|
78 |
+
echo 2. استخدم Android Studio للبناء
|
79 |
+
echo 3. راجع رسائل الخطأ أعلاه
|
80 |
+
echo.
|
81 |
+
)
|
82 |
+
|
83 |
+
echo.
|
84 |
+
pause
|
BUILD_FROM_GITHUB.bat
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
echo ========================================
|
3 |
+
echo بناء APK من GitHub - محفظتي الموحدة
|
4 |
+
echo ========================================
|
5 |
+
echo.
|
6 |
+
|
7 |
+
echo [1/6] التحقق من الأدوات المطلوبة...
|
8 |
+
|
9 |
+
:: التحقق من Git
|
10 |
+
git --version >nul 2>&1
|
11 |
+
if %errorlevel% neq 0 (
|
12 |
+
echo ❌ Git غير مثبت!
|
13 |
+
echo يرجى تثبيت Git من: https://git-scm.com/
|
14 |
+
pause
|
15 |
+
exit /b 1
|
16 |
+
)
|
17 |
+
echo ✅ Git متوفر
|
18 |
+
|
19 |
+
:: التحقق من Node.js
|
20 |
+
node --version >nul 2>&1
|
21 |
+
if %errorlevel% neq 0 (
|
22 |
+
echo ❌ Node.js غير مثبت!
|
23 |
+
echo يرجى تثبيت Node.js من: https://nodejs.org/
|
24 |
+
pause
|
25 |
+
exit /b 1
|
26 |
+
)
|
27 |
+
echo ✅ Node.js متوفر
|
28 |
+
|
29 |
+
:: التحقق من Java
|
30 |
+
java -version >nul 2>&1
|
31 |
+
if %errorlevel% neq 0 (
|
32 |
+
echo ❌ Java غير مثبت!
|
33 |
+
echo يرجى تثبيت Java JDK من: https://adoptium.net/
|
34 |
+
pause
|
35 |
+
exit /b 1
|
36 |
+
)
|
37 |
+
echo ✅ Java متوفر
|
38 |
+
|
39 |
+
echo.
|
40 |
+
echo [2/6] استنساخ المشروع من GitHub...
|
41 |
+
if exist "almada-unified-wallet" (
|
42 |
+
echo مجلد المشروع موجود، سيتم حذفه وإعادة الاستنساخ...
|
43 |
+
rmdir /s /q "almada-unified-wallet"
|
44 |
+
)
|
45 |
+
|
46 |
+
git clone https://github.com/moh77544/---.git almada-unified-wallet
|
47 |
+
if %errorlevel% neq 0 (
|
48 |
+
echo ❌ فشل في استنساخ المشروع!
|
49 |
+
pause
|
50 |
+
exit /b 1
|
51 |
+
)
|
52 |
+
echo ✅ تم استنساخ المشروع
|
53 |
+
|
54 |
+
echo.
|
55 |
+
echo [3/6] الانتقال إلى مجلد المشروع...
|
56 |
+
cd almada-unified-wallet
|
57 |
+
|
58 |
+
echo.
|
59 |
+
echo [4/6] تثبيت التبعيات...
|
60 |
+
call npm install
|
61 |
+
if %errorlevel% neq 0 (
|
62 |
+
echo ❌ فشل في تثبيت التبعيات!
|
63 |
+
pause
|
64 |
+
exit /b 1
|
65 |
+
)
|
66 |
+
|
67 |
+
call npm install -g @ionic/cli @capacitor/cli
|
68 |
+
echo ✅ تم تثبيت التبعيات
|
69 |
+
|
70 |
+
echo.
|
71 |
+
echo [5/6] إعداد ملفات الويب...
|
72 |
+
if not exist "www" mkdir www
|
73 |
+
copy index.html www\ >nul 2>&1
|
74 |
+
copy styles.css www\ >nul 2>&1
|
75 |
+
copy app.js www\ >nul 2>&1
|
76 |
+
copy auth.js www\ >nul 2>&1
|
77 |
+
copy wallets.js www\ >nul 2>&1
|
78 |
+
copy notifications.js www\ >nul 2>&1
|
79 |
+
copy demo.html www\ >nul 2>&1
|
80 |
+
copy src\manifest.json www\ >nul 2>&1
|
81 |
+
echo ✅ تم إعداد ملفات الويب
|
82 |
+
|
83 |
+
echo.
|
84 |
+
echo [6/6] مزامنة وبناء APK...
|
85 |
+
call npx cap sync android
|
86 |
+
if %errorlevel% neq 0 (
|
87 |
+
echo ❌ فشل في مزامنة Capacitor!
|
88 |
+
pause
|
89 |
+
exit /b 1
|
90 |
+
)
|
91 |
+
|
92 |
+
cd android
|
93 |
+
call gradlew assembleDebug
|
94 |
+
if %errorlevel% neq 0 (
|
95 |
+
echo ❌ فشل في بناء APK!
|
96 |
+
echo تأكد من تثبيت Android SDK
|
97 |
+
pause
|
98 |
+
exit /b 1
|
99 |
+
)
|
100 |
+
|
101 |
+
echo.
|
102 |
+
echo ========================================
|
103 |
+
echo 🎉 تم بناء APK بنجاح!
|
104 |
+
echo ========================================
|
105 |
+
echo.
|
106 |
+
echo 📱 ملف APK متوفر في:
|
107 |
+
echo %cd%\app\build\outputs\apk\debug\app-debug.apk
|
108 |
+
echo.
|
109 |
+
echo 📋 معلومات التطبيق:
|
110 |
+
echo - الاسم: محفظتي الموحدة
|
111 |
+
echo - الحجم: ~15-20 MB
|
112 |
+
echo - النوع: Debug APK
|
113 |
+
echo.
|
114 |
+
echo 🔑 بيانات التجربة:
|
115 |
+
echo - رقم الهاتف: 777123456
|
116 |
+
echo - رمز PIN: 1234
|
117 |
+
echo.
|
118 |
+
echo 📲 لتثبيت التطبيق:
|
119 |
+
echo 1. انسخ ملف APK إلى هاتفك
|
120 |
+
echo 2. فعل "مصادر غير معروفة" في إعدادات الأمان
|
121 |
+
echo 3. اضغط على ملف APK لتثبيته
|
122 |
+
echo.
|
123 |
+
|
124 |
+
:: فتح مجلد APK
|
125 |
+
explorer "app\build\outputs\apk\debug\"
|
126 |
+
|
127 |
+
echo.
|
128 |
+
pause
|
BUILD_INSTRUCTIONS.md
ADDED
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# تعليمات بناء تطبيق محفظتي الموحدة
|
2 |
+
|
3 |
+
## متطلبات النظام
|
4 |
+
|
5 |
+
### الأدوات المطلوبة:
|
6 |
+
- **Node.js** (الإصدار 18 أو أحدث)
|
7 |
+
- **npm** أو **yarn**
|
8 |
+
- **Ionic CLI** (الإصدار 7 أو أحدث)
|
9 |
+
- **Angular CLI** (الإصدار 17 أو أحدث)
|
10 |
+
- **Capacitor CLI** (الإصدار 5 أو أحدث)
|
11 |
+
|
12 |
+
### للأندرويد:
|
13 |
+
- **Android Studio** (أحدث إصدار)
|
14 |
+
- **Android SDK** (API Level 24 أو أحدث)
|
15 |
+
- **Java JDK** (الإصدار 11 أو أحدث)
|
16 |
+
|
17 |
+
### لـ iOS:
|
18 |
+
- **Xcode** (أحدث إصدار)
|
19 |
+
- **iOS SDK** (iOS 13 أو أحدث)
|
20 |
+
- **macOS** (مطلوب لبناء تطبيقات iOS)
|
21 |
+
|
22 |
+
## خطوات التثبيت
|
23 |
+
|
24 |
+
### 1. تثبيت الأدوات العامة
|
25 |
+
```bash
|
26 |
+
# تثبيت Node.js من https://nodejs.org
|
27 |
+
|
28 |
+
# تثبيت Ionic CLI
|
29 |
+
npm install -g @ionic/cli
|
30 |
+
|
31 |
+
# تثبيت Angular CLI
|
32 |
+
npm install -g @angular/cli
|
33 |
+
|
34 |
+
# تثبيت Capacitor CLI
|
35 |
+
npm install -g @capacitor/cli
|
36 |
+
```
|
37 |
+
|
38 |
+
### 2. إعداد المشروع
|
39 |
+
```bash
|
40 |
+
# الانتقال إلى مجلد المشروع
|
41 |
+
cd almada
|
42 |
+
|
43 |
+
# تثبيت التبعيات
|
44 |
+
npm install
|
45 |
+
|
46 |
+
# أو باستخدام yarn
|
47 |
+
yarn install
|
48 |
+
```
|
49 |
+
|
50 |
+
### 3. إعداد Capacitor
|
51 |
+
```bash
|
52 |
+
# تهيئة Capacitor
|
53 |
+
npx cap init "محفظتي الموحدة" "com.almada.unifiedwallet"
|
54 |
+
|
55 |
+
# إضافة منصات
|
56 |
+
npx cap add android
|
57 |
+
npx cap add ios
|
58 |
+
```
|
59 |
+
|
60 |
+
## بناء التطبيق
|
61 |
+
|
62 |
+
### 1. بناء تطبيق الويب
|
63 |
+
```bash
|
64 |
+
# بناء للتطوير
|
65 |
+
ionic build
|
66 |
+
|
67 |
+
# بناء للإنتاج
|
68 |
+
ionic build --prod
|
69 |
+
```
|
70 |
+
|
71 |
+
### 2. بناء تطبيق الأندرويد
|
72 |
+
|
73 |
+
#### أ. إعداد Android Studio
|
74 |
+
```bash
|
75 |
+
# نسخ الملفات إلى مجلد الأندرويد
|
76 |
+
npx cap copy android
|
77 |
+
|
78 |
+
# مزامنة المشروع
|
79 |
+
npx cap sync android
|
80 |
+
|
81 |
+
# فتح في Android Studio
|
82 |
+
npx cap open android
|
83 |
+
```
|
84 |
+
|
85 |
+
#### ب. بناء APK من سطر الأوامر
|
86 |
+
```bash
|
87 |
+
# بناء APK للتطوير
|
88 |
+
cd android
|
89 |
+
./gradlew assembleDebug
|
90 |
+
|
91 |
+
# بناء APK للإنتاج
|
92 |
+
./gradlew assembleRelease
|
93 |
+
|
94 |
+
# بناء AAB للنشر في Google Play
|
95 |
+
./gradlew bundleRelease
|
96 |
+
```
|
97 |
+
|
98 |
+
#### ج. بناء APK من Android Studio
|
99 |
+
1. افتح Android Studio
|
100 |
+
2. اختر **Build > Build Bundle(s) / APK(s) > Build APK(s)**
|
101 |
+
3. انتظر حتى اكتمال البناء
|
102 |
+
4. ستجد ملف APK في: `android/app/build/outputs/apk/`
|
103 |
+
|
104 |
+
### 3. بناء تطبيق iOS
|
105 |
+
|
106 |
+
#### أ. إعداد Xcode
|
107 |
+
```bash
|
108 |
+
# نسخ الملفات إلى مجلد iOS
|
109 |
+
npx cap copy ios
|
110 |
+
|
111 |
+
# مزامنة المشروع
|
112 |
+
npx cap sync ios
|
113 |
+
|
114 |
+
# فتح في Xcode
|
115 |
+
npx cap open ios
|
116 |
+
```
|
117 |
+
|
118 |
+
#### ب. بناء IPA من Xcode
|
119 |
+
1. افتح Xcode
|
120 |
+
2. اختر جهاز أو محاكي
|
121 |
+
3. اختر **Product > Archive**
|
122 |
+
4. بعد اكتمال الأرشفة، اختر **Distribute App**
|
123 |
+
5. اتبع التعليمات لإنشاء ملف IPA
|
124 |
+
|
125 |
+
## تشغيل التطبيق للتطوير
|
126 |
+
|
127 |
+
### 1. تشغيل في المتصفح
|
128 |
+
```bash
|
129 |
+
# تشغيل خادم التطوير
|
130 |
+
ionic serve
|
131 |
+
|
132 |
+
# تشغيل مع إعادة التحميل التلقائي
|
133 |
+
ionic serve --lab
|
134 |
+
```
|
135 |
+
|
136 |
+
### 2. تشغيل على الأندرويد
|
137 |
+
```bash
|
138 |
+
# تشغيل على جهاز أو محاكي
|
139 |
+
ionic cap run android
|
140 |
+
|
141 |
+
# تشغيل مع إعادة التحميل المباشر
|
142 |
+
ionic cap run android --livereload --external
|
143 |
+
```
|
144 |
+
|
145 |
+
### 3. تشغيل على iOS
|
146 |
+
```bash
|
147 |
+
# تشغيل على جهاز أو محاكي
|
148 |
+
ionic cap run ios
|
149 |
+
|
150 |
+
# تشغيل مع إعادة التحميل المباشر
|
151 |
+
ionic cap run ios --livereload --external
|
152 |
+
```
|
153 |
+
|
154 |
+
## إعدادات الإنتاج
|
155 |
+
|
156 |
+
### 1. تحديث المتغيرات
|
157 |
+
قم بتحديث ملف `src/environments/environment.prod.ts`:
|
158 |
+
```typescript
|
159 |
+
export const environment = {
|
160 |
+
production: true,
|
161 |
+
apiUrl: 'https://api.almada.com',
|
162 |
+
// ... باقي الإعدادات
|
163 |
+
};
|
164 |
+
```
|
165 |
+
|
166 |
+
### 2. إعداد الأيقونات والشاشات
|
167 |
+
```bash
|
168 |
+
# إنشاء الأيقونات والشاشات تلقائياً
|
169 |
+
npm install -g cordova-res
|
170 |
+
cordova-res android --skip-config --copy
|
171 |
+
cordova-res ios --skip-config --copy
|
172 |
+
```
|
173 |
+
|
174 |
+
### 3. توقيع التطبيق للأندرويد
|
175 |
+
```bash
|
176 |
+
# إنشاء مفتاح التوقيع
|
177 |
+
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
|
178 |
+
|
179 |
+
# إضافة إعدادات التوقيع في android/app/build.gradle
|
180 |
+
```
|
181 |
+
|
182 |
+
### 4. إعداد التوقيع لـ iOS
|
183 |
+
1. افتح Xcode
|
184 |
+
2. اذهب إلى **Signing & Capabilities**
|
185 |
+
3. اختر **Team** و **Bundle Identifier**
|
186 |
+
4. تأكد من إعداد **Provisioning Profile**
|
187 |
+
|
188 |
+
## اختبار التطبيق
|
189 |
+
|
190 |
+
### 1. اختبار الوحدة
|
191 |
+
```bash
|
192 |
+
# تشغيل اختبارات الوحدة
|
193 |
+
npm test
|
194 |
+
|
195 |
+
# تشغيل مع المراقبة
|
196 |
+
npm test -- --watch
|
197 |
+
```
|
198 |
+
|
199 |
+
### 2. اختبار النهاية إلى النهاية
|
200 |
+
```bash
|
201 |
+
# تشغيل اختبارات e2e
|
202 |
+
npm run e2e
|
203 |
+
```
|
204 |
+
|
205 |
+
### 3. اختبار على الأجهزة
|
206 |
+
```bash
|
207 |
+
# تثبيت على جهاز أندرويد
|
208 |
+
adb install android/app/build/outputs/apk/debug/app-debug.apk
|
209 |
+
|
210 |
+
# عرض سجلات الأندرويد
|
211 |
+
adb logcat
|
212 |
+
```
|
213 |
+
|
214 |
+
## نشر التطبيق
|
215 |
+
|
216 |
+
### 1. Google Play Store
|
217 |
+
1. إنشاء حساب مطور في Google Play Console
|
218 |
+
2. رفع ملف AAB
|
219 |
+
3. ملء معلومات التطبيق
|
220 |
+
4. إرسال للمراجعة
|
221 |
+
|
222 |
+
### 2. Apple App Store
|
223 |
+
1. إنشاء حساب مطور في App Store Connect
|
224 |
+
2. رفع التطبيق عبر Xcode أو Application Loader
|
225 |
+
3. ملء معلومات التطبيق
|
226 |
+
4. إرسال للمراجعة
|
227 |
+
|
228 |
+
## استكشاف الأخطاء
|
229 |
+
|
230 |
+
### مشاكل شائعة:
|
231 |
+
|
232 |
+
#### 1. خطأ في بناء الأندرويد
|
233 |
+
```bash
|
234 |
+
# تنظيف المشروع
|
235 |
+
cd android
|
236 |
+
./gradlew clean
|
237 |
+
|
238 |
+
# إعادة بناء
|
239 |
+
./gradlew build
|
240 |
+
```
|
241 |
+
|
242 |
+
#### 2. مشاكل Capacitor
|
243 |
+
```bash
|
244 |
+
# إعادة مزامنة
|
245 |
+
npx cap sync
|
246 |
+
|
247 |
+
# تحديث Capacitor
|
248 |
+
npm update @capacitor/core @capacitor/cli
|
249 |
+
```
|
250 |
+
|
251 |
+
#### 3. مشاكل الأذونات
|
252 |
+
تأكد من إضافة الأذونات المطلوبة في:
|
253 |
+
- `android/app/src/main/AndroidManifest.xml` للأندرويد
|
254 |
+
- `ios/App/App/Info.plist` لـ iOS
|
255 |
+
|
256 |
+
## الدعم والمساعدة
|
257 |
+
|
258 |
+
للحصول على المساعدة:
|
259 |
+
1. راجع [وثائق Ionic](https://ionicframework.com/docs)
|
260 |
+
2. راجع [وثائق Capacitor](https://capacitorjs.com/docs)
|
261 |
+
3. تواصل مع فريق التطوير
|
262 |
+
|
263 |
+
---
|
264 |
+
|
265 |
+
**ملاحظة**: تأكد من تحديث جميع التبعيات بانتظام للحصول على أحدث الميزات وإصلاحات الأمان.
|
CLOUD_BUILD_QUICK.md
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ☁️ البناء السحابي السريع - 15 دقيقة فقط!
|
2 |
+
|
3 |
+
## 🚀 **الهدف:** APK جاهز من السحابة في 15 دقيقة!
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## ⚡ **الخطوات السريعة:**
|
8 |
+
|
9 |
+
### **1️⃣ إنشاء حساب GitHub** (2 دقيقة)
|
10 |
+
```
|
11 |
+
🌐 اذهب إلى: https://github.com
|
12 |
+
📝 اضغط "Sign up" وأنشئ حساب
|
13 |
+
✅ تأكد من البريد الإلكتروني
|
14 |
+
```
|
15 |
+
|
16 |
+
### **2️⃣ إنشاء Repository** (1 دقيقة)
|
17 |
+
```
|
18 |
+
➕ اضغط "New repository"
|
19 |
+
📝 الاسم: almada-unified-wallet
|
20 |
+
📄 الوصف: محفظتي الموحدة
|
21 |
+
🌍 اختر "Public"
|
22 |
+
✅ اضغط "Create repository"
|
23 |
+
```
|
24 |
+
|
25 |
+
### **3️⃣ رفع الملفات** (5 دقائق)
|
26 |
+
```
|
27 |
+
📁 اضغط "uploading an existing file"
|
28 |
+
📤 اسحب وأفلت هذه الملفات:
|
29 |
+
✅ .github/workflows/build-apk.yml
|
30 |
+
✅ index.html
|
31 |
+
✅ styles.css
|
32 |
+
✅ app.js
|
33 |
+
✅ auth.js
|
34 |
+
✅ wallets.js
|
35 |
+
✅ notifications.js
|
36 |
+
✅ package.json
|
37 |
+
✅ capacitor.config.ts
|
38 |
+
✅ مجلد android كامل
|
39 |
+
💬 رسالة: "Initial commit"
|
40 |
+
✅ اضغط "Commit changes"
|
41 |
+
```
|
42 |
+
|
43 |
+
### **4️⃣ تشغيل البناء** (10 دقائق)
|
44 |
+
```
|
45 |
+
🔧 اذهب إلى تبويب "Actions"
|
46 |
+
🚀 اضغط "Run workflow"
|
47 |
+
⏰ انتظر 10-15 دقيقة
|
48 |
+
✅ ابحث عن علامة ✅ خضراء
|
49 |
+
```
|
50 |
+
|
51 |
+
### **5️⃣ تحميل APK** (1 دقيقة)
|
52 |
+
```
|
53 |
+
📱 اضغط على Build الناجح
|
54 |
+
📦 في الأسفل: "Artifacts"
|
55 |
+
⬇️ حمل: almada-unified-wallet-apk
|
56 |
+
📱 استخرج APK وثبته!
|
57 |
+
```
|
58 |
+
|
59 |
+
---
|
60 |
+
|
61 |
+
## 🎯 **النتيجة:**
|
62 |
+
|
63 |
+
✅ **ملف APK** جاهز للتثبيت
|
64 |
+
✅ **بناء تلقائي** عند كل تحديث
|
65 |
+
✅ **مجاني تماماً** مع GitHub
|
66 |
+
✅ **رابط تحميل** مباشر
|
67 |
+
|
68 |
+
---
|
69 |
+
|
70 |
+
## 📱 **معلومات APK:**
|
71 |
+
|
72 |
+
| المعلومة | القيمة |
|
73 |
+
|---------|--------|
|
74 |
+
| **الاسم** | app-debug.apk |
|
75 |
+
| **الحجم** | ~15-20 MB |
|
76 |
+
| **بيانات التجربة** | 777123456 / 1234 |
|
77 |
+
| **متوافق مع** | Android 7.0+ |
|
78 |
+
|
79 |
+
---
|
80 |
+
|
81 |
+
## 🔄 **للتحديثات المستقبلية:**
|
82 |
+
|
83 |
+
```
|
84 |
+
1. عدل أي ملف في GitHub
|
85 |
+
2. Commit التغييرات
|
86 |
+
3. APK جديد سيُبنى تلقائياً!
|
87 |
+
```
|
88 |
+
|
89 |
+
---
|
90 |
+
|
91 |
+
## 🆘 **مشاكل شائعة:**
|
92 |
+
|
93 |
+
### **❌ Build فشل:**
|
94 |
+
```
|
95 |
+
✅ تحقق من رفع جميع الملفات
|
96 |
+
✅ راجع logs في Actions
|
97 |
+
✅ تأكد من package.json صحيح
|
98 |
+
```
|
99 |
+
|
100 |
+
### **❌ لا يوجد APK:**
|
101 |
+
```
|
102 |
+
✅ انتظر اكتمال Build (علامة ✅)
|
103 |
+
✅ ابحث في "Artifacts"
|
104 |
+
✅ حدث الصفحة
|
105 |
+
```
|
106 |
+
|
107 |
+
---
|
108 |
+
|
109 |
+
## 🎊 **مبروك!**
|
110 |
+
|
111 |
+
عند اكتمال هذه الخطوات:
|
112 |
+
- 📱 **APK جاهز** للتثبيت
|
113 |
+
- ☁️ **بناء سحابي** مجاني
|
114 |
+
- 🔄 **تحديثات تلقائية**
|
115 |
+
- 🌍 **متاح للعالم**
|
116 |
+
|
117 |
+
---
|
118 |
+
|
119 |
+
## 📞 **تحتاج مساعدة؟**
|
120 |
+
|
121 |
+
راجع الدليل المفصل: `GITHUB_SETUP_GUIDE.md`
|
122 |
+
|
123 |
+
**🚀 تطبيقك على بُعد 15 دقيقة من الواقع!**
|
GITHUB_SETUP_GUIDE.md
ADDED
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ☁️ دليل إعداد البناء السحابي - GitHub Actions
|
2 |
+
|
3 |
+
## 🎯 **الهدف:** بناء APK تلقائياً في السحابة مجاناً!
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## 📋 **الخطوات المطلوبة:**
|
8 |
+
|
9 |
+
### **الخطوة 1: إنشاء حساب GitHub** ⏱️ (2 دقيقة)
|
10 |
+
|
11 |
+
1. **اذهب إلى:** https://github.com
|
12 |
+
2. **اضغط "Sign up"** وأنشئ حساب جديد
|
13 |
+
3. **تأكد من البريد الإلكتروني**
|
14 |
+
|
15 |
+
### **الخطوة 2: إنشاء Repository جديد** ⏱️ (1 دقيقة)
|
16 |
+
|
17 |
+
1. **اضغط "New repository"** (الزر الأخضر)
|
18 |
+
2. **اسم المستودع:** `almada-unified-wallet`
|
19 |
+
3. **الوصف:** `محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية`
|
20 |
+
4. **اختر "Public"** (مجاني)
|
21 |
+
5. **فعل "Add a README file"**
|
22 |
+
6. **اضغط "Create repository"**
|
23 |
+
|
24 |
+
### **الخطوة 3: رفع الملفات** ⏱️ (5 دقائق)
|
25 |
+
|
26 |
+
#### **الطريقة الأسهل: عبر الموقع**
|
27 |
+
|
28 |
+
1. **في صفحة Repository، اضغط "uploading an existing file"**
|
29 |
+
2. **اسحب وأفلت الملفات التالية:**
|
30 |
+
```
|
31 |
+
📁 .github/workflows/build-apk.yml
|
32 |
+
📄 index.html
|
33 |
+
📄 styles.css
|
34 |
+
📄 app.js
|
35 |
+
📄 auth.js
|
36 |
+
📄 wallets.js
|
37 |
+
📄 notifications.js
|
38 |
+
📄 demo.html
|
39 |
+
📄 package.json
|
40 |
+
📄 capacitor.config.ts
|
41 |
+
📄 .gitignore
|
42 |
+
📁 src/manifest.json
|
43 |
+
📁 android/ (كامل)
|
44 |
+
```
|
45 |
+
3. **اكتب رسالة:** `Initial commit - محفظتي الموحدة`
|
46 |
+
4. **اضغط "Commit changes"**
|
47 |
+
|
48 |
+
#### **الطريقة المتقدمة: Git Command Line**
|
49 |
+
|
50 |
+
```bash
|
51 |
+
# في مجلد المشروع
|
52 |
+
git init
|
53 |
+
git add .
|
54 |
+
git commit -m "Initial commit - محفظتي الموحدة"
|
55 |
+
git branch -M main
|
56 |
+
git remote add origin https://github.com/USERNAME/almada-unified-wallet.git
|
57 |
+
git push -u origin main
|
58 |
+
```
|
59 |
+
|
60 |
+
### **الخطوة 4: تشغيل البناء التلقائي** ⏱️ (10-15 دقيقة)
|
61 |
+
|
62 |
+
1. **اذهب إلى تبويب "Actions"** في Repository
|
63 |
+
2. **ستجد workflow اسمه:** `🚀 Build APK - محفظتي الموحدة`
|
64 |
+
3. **اضغط "Run workflow"** إذا لم يبدأ تلقائياً
|
65 |
+
4. **انتظر اكتمال البناء** (10-15 دقيقة)
|
66 |
+
|
67 |
+
### **الخطوة 5: تحميل APK** ⏱️ (1 دقيقة)
|
68 |
+
|
69 |
+
عند اكتمال البناء:
|
70 |
+
|
71 |
+
1. **اضغط على Build الناجح** (علامة ✅ خضراء)
|
72 |
+
2. **في الأسفل، ستجد "Artifacts"**
|
73 |
+
3. **اضغط على:** `almada-unified-wallet-apk`
|
74 |
+
4. **حمل ملف ZIP واستخرج APK منه**
|
75 |
+
|
76 |
+
---
|
77 |
+
|
78 |
+
## 🎯 **البدائل السحابية الأخرى:**
|
79 |
+
|
80 |
+
### **Ionic Appflow (مجاني للمشاريع الصغيرة):**
|
81 |
+
|
82 |
+
```bash
|
83 |
+
# تثبيت Ionic CLI
|
84 |
+
npm install -g @ionic/cli
|
85 |
+
|
86 |
+
# تسجيل الدخول
|
87 |
+
ionic login
|
88 |
+
|
89 |
+
# ربط المشروع
|
90 |
+
ionic link
|
91 |
+
|
92 |
+
# بناء في السحابة
|
93 |
+
ionic capacitor build android --prod
|
94 |
+
```
|
95 |
+
|
96 |
+
### **CodeMagic (مجاني 500 دقيقة/شهر):**
|
97 |
+
|
98 |
+
1. اذهب إلى: https://codemagic.io
|
99 |
+
2. ربط حساب GitHub
|
100 |
+
3. اختر Repository
|
101 |
+
4. إعداد workflow للأندرويد
|
102 |
+
5. بناء تلقائي
|
103 |
+
|
104 |
+
---
|
105 |
+
|
106 |
+
## 📱 **ما ستحصل عليه:**
|
107 |
+
|
108 |
+
### **من GitHub Actions:**
|
109 |
+
- ✅ **بناء تلقائي** عند كل تحديث
|
110 |
+
- ✅ **APK مجاني** بدون حدود
|
111 |
+
- ✅ **تاريخ الإصدارات** كامل
|
112 |
+
- ✅ **رابط تحميل** مباشر
|
113 |
+
|
114 |
+
### **معلومات APK:**
|
115 |
+
- 📱 **الاسم:** `app-debug.apk`
|
116 |
+
- 💾 **الحجم:** ~15-20 MB
|
117 |
+
- 🔧 **النوع:** Debug APK
|
118 |
+
- 📲 **جاهز للتثبيت** على أي هاتف أندرويد
|
119 |
+
|
120 |
+
---
|
121 |
+
|
122 |
+
## 🔄 **التحديثات المستقبلية:**
|
123 |
+
|
124 |
+
### **لإضافة ميزات جديدة:**
|
125 |
+
1. **عدل الملفات** في Repository
|
126 |
+
2. **Commit التغييرات**
|
127 |
+
3. **APK جديد** سيُبنى تلقائياً!
|
128 |
+
|
129 |
+
### **لإنشاء Release:**
|
130 |
+
```bash
|
131 |
+
# إنشاء tag جديد
|
132 |
+
git tag v1.0.1
|
133 |
+
git push origin v1.0.1
|
134 |
+
|
135 |
+
# سيُنشئ Release تلقائياً مع APK
|
136 |
+
```
|
137 |
+
|
138 |
+
---
|
139 |
+
|
140 |
+
## 🆘 **حل المشاكل:**
|
141 |
+
|
142 |
+
### **مشكلة: Build فشل**
|
143 |
+
```
|
144 |
+
الحل:
|
145 |
+
1. تحقق من logs في Actions
|
146 |
+
2. تأكد من رفع جميع الملفات
|
147 |
+
3. تحقق من package.json
|
148 |
+
```
|
149 |
+
|
150 |
+
### **مشكلة: لا يوجد Artifacts**
|
151 |
+
```
|
152 |
+
الحل:
|
153 |
+
1. تأكد من نجاح Build (علامة ✅)
|
154 |
+
2. انتظر اكتمال جميع الخطوات
|
155 |
+
3. حدث الصفحة
|
156 |
+
```
|
157 |
+
|
158 |
+
### **مشكلة: APK لا يعمل**
|
159 |
+
```
|
160 |
+
الحل:
|
161 |
+
1. تأكد من تفعيل "مصادر غير معروفة"
|
162 |
+
2. تحقق من توافق إصدار الأندرويد
|
163 |
+
3. أعد تحميل APK
|
164 |
+
```
|
165 |
+
|
166 |
+
---
|
167 |
+
|
168 |
+
## 💡 **نصائح مهمة:**
|
169 |
+
|
170 |
+
### **للنجاح:**
|
171 |
+
- 📁 **ارفع جميع الملفات** المطلوبة
|
172 |
+
- 🌐 **تأكد من اتصال الإنترنت** أثناء البناء
|
173 |
+
- ⏰ **انتظر اكتمال** جميع الخطوات
|
174 |
+
|
175 |
+
### **للأمان:**
|
176 |
+
- 🔒 **لا تشارك** معلومات حساسة في Repository العام
|
177 |
+
- 🔑 **استخدم Secrets** للمعلومات الحساسة
|
178 |
+
- 🛡️ **راجع الأذونات** بانتظام
|
179 |
+
|
180 |
+
### **للتطوير:**
|
181 |
+
- 📝 **اكتب وصف واضح** للـ commits
|
182 |
+
- 🏷️ **استخدم tags** للإصدارات
|
183 |
+
- 📚 **حدث README** بانتظام
|
184 |
+
|
185 |
+
---
|
186 |
+
|
187 |
+
## 🎉 **النتيجة النهائية:**
|
188 |
+
|
189 |
+
عند اكتمال هذه الخطوات، ستحصل على:
|
190 |
+
|
191 |
+
- ☁️ **نظام بناء سحابي** مجاني
|
192 |
+
- 🔄 **APK تلقائي** عند كل تحديث
|
193 |
+
- 📱 **رابط تحميل** مباشر
|
194 |
+
- 🌍 **متاح للعالم** عبر GitHub
|
195 |
+
|
196 |
+
---
|
197 |
+
|
198 |
+
## 📞 **تحتاج مساعدة؟**
|
199 |
+
|
200 |
+
إذا واجهت أي مشكلة:
|
201 |
+
1. **راجع logs** في GitHub Actions
|
202 |
+
2. **تحقق من الملفات** المرفوعة
|
203 |
+
3. **تواصل للمساعدة**
|
204 |
+
|
205 |
+
**🚀 مبروك! تطبيقك سيُبنى في السحابة!**
|
206 |
+
|
207 |
+
---
|
208 |
+
|
209 |
+
**💡 ملاحظة:** GitHub Actions مجاني للمشاريع العامة مع 2000 دقيقة/شهر للمشاريع الخاصة.
|
INSTALL_ANDROID_SDK.bat
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
echo ========================================
|
3 |
+
echo تثبيت Android SDK - محفظتي الموحدة
|
4 |
+
echo ========================================
|
5 |
+
echo.
|
6 |
+
|
7 |
+
echo هذا الملف سيساعدك في تثبيت Android SDK بدون Android Studio
|
8 |
+
echo.
|
9 |
+
|
10 |
+
echo [الخطوة 1] إنشاء مجلد Android SDK...
|
11 |
+
if not exist "C:\Android" mkdir "C:\Android"
|
12 |
+
if not exist "C:\Android\cmdline-tools" mkdir "C:\Android\cmdline-tools"
|
13 |
+
echo ✅ تم إنشاء المجلدات
|
14 |
+
|
15 |
+
echo.
|
16 |
+
echo [الخطوة 2] تحميل Command Line Tools...
|
17 |
+
echo يرجى تحميل Command Line Tools من:
|
18 |
+
echo https://developer.android.com/studio#command-tools
|
19 |
+
echo.
|
20 |
+
echo اختر: "Command line tools only" > Windows
|
21 |
+
echo.
|
22 |
+
echo بعد التحميل:
|
23 |
+
echo 1. استخرج الملف المضغوط
|
24 |
+
echo 2. انسخ محتويات مجلد cmdline-tools إلى:
|
25 |
+
echo C:\Android\cmdline-tools\latest\
|
26 |
+
echo.
|
27 |
+
pause
|
28 |
+
|
29 |
+
echo.
|
30 |
+
echo [الخطوة 3] إعداد متغيرات البيئة...
|
31 |
+
echo سيتم إضافة متغيرات البيئة التالية:
|
32 |
+
echo ANDROID_HOME=C:\Android
|
33 |
+
echo ANDROID_SDK_ROOT=C:\Android
|
34 |
+
echo.
|
35 |
+
|
36 |
+
setx ANDROID_HOME "C:\Android" /M >nul 2>&1
|
37 |
+
setx ANDROID_SDK_ROOT "C:\Android" /M >nul 2>&1
|
38 |
+
|
39 |
+
echo ✅ تم إعداد متغيرات البيئة
|
40 |
+
|
41 |
+
echo.
|
42 |
+
echo [الخطوة 4] إضافة إلى PATH...
|
43 |
+
set "newPath=C:\Android\cmdline-tools\latest\bin;C:\Android\platform-tools"
|
44 |
+
|
45 |
+
for /f "tokens=2*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH 2^>nul') do set "currentPath=%%b"
|
46 |
+
|
47 |
+
if not defined currentPath set "currentPath="
|
48 |
+
|
49 |
+
echo %currentPath% | find /i "%newPath%" >nul
|
50 |
+
if %errorlevel% neq 0 (
|
51 |
+
setx PATH "%currentPath%;%newPath%" /M >nul 2>&1
|
52 |
+
echo ✅ تم إضافة Android SDK إلى PATH
|
53 |
+
) else (
|
54 |
+
echo ✅ Android SDK موجود بالفعل في PATH
|
55 |
+
)
|
56 |
+
|
57 |
+
echo.
|
58 |
+
echo [الخطوة 5] إعادة تشغيل Command Prompt...
|
59 |
+
echo يرجى إغلاق هذه النافذة وفتح Command Prompt جديد
|
60 |
+
echo ثم تشغيل الأوامر التالية:
|
61 |
+
echo.
|
62 |
+
echo sdkmanager --version
|
63 |
+
echo sdkmanager "platform-tools"
|
64 |
+
echo sdkmanager "platforms;android-33"
|
65 |
+
echo sdkmanager "build-tools;33.0.0"
|
66 |
+
echo.
|
67 |
+
|
68 |
+
echo ========================================
|
69 |
+
echo 📋 ملخص ما تم:
|
70 |
+
echo ========================================
|
71 |
+
echo ✅ إنشاء مجلد C:\Android
|
72 |
+
echo ✅ إعداد ANDROID_HOME
|
73 |
+
echo ✅ إعداد ANDROID_SDK_ROOT
|
74 |
+
echo ✅ إضافة إلى PATH
|
75 |
+
echo.
|
76 |
+
echo 📝 المطلوب منك:
|
77 |
+
echo 1. تحميل Command Line Tools
|
78 |
+
echo 2. استخراج إلى C:\Android\cmdline-tools\latest\
|
79 |
+
echo 3. إعادة تشغيل Command Prompt
|
80 |
+
echo 4. تشغيل أوامر sdkmanager
|
81 |
+
echo.
|
82 |
+
echo بعد ذلك يمكنك تشغيل QUICK_APK_BUILD.bat
|
83 |
+
echo ========================================
|
84 |
+
pause
|
PROJECT_SUMMARY.md
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📊 ملخص مشروع محفظتي الموحدة - التحويل إلى APK
|
2 |
+
|
3 |
+
## 🎯 **الهدف المحقق**
|
4 |
+
تم تحويل تطبيق **محفظتي الموحدة** من تطبيق ويب إلى **تطبيق أندرويد APK** جاهز للتثبيت والاستخدام.
|
5 |
+
|
6 |
+
---
|
7 |
+
|
8 |
+
## ✅ **ما تم إنجازه بنجاح**
|
9 |
+
|
10 |
+
### **1. إعداد البيئة التقنية**
|
11 |
+
- ✅ تحويل المشروع إلى **Capacitor** (تقنية التطبيقات الهجينة)
|
12 |
+
- ✅ إعداد **package.json** مع جميع التبعيات المطلوبة
|
13 |
+
- ✅ تثبيت **Java JDK 11** للبناء
|
14 |
+
- ✅ إعداد **capacitor.config.ts** للتكوين
|
15 |
+
|
16 |
+
### **2. إعداد مشروع الأندرويد**
|
17 |
+
- ✅ إضافة منصة الأندرويد: `cap add android`
|
18 |
+
- ✅ إعداد **AndroidManifest.xml** مع الأذونات المطلوبة
|
19 |
+
- ✅ تكوين **strings.xml** باللغة العربية
|
20 |
+
- ✅ نسخ ملفات التطبيق إلى مجلد `www`
|
21 |
+
|
22 |
+
### **3. إضافة الأذونات المطلوبة**
|
23 |
+
```xml
|
24 |
+
<!-- الأذونات الأساسية -->
|
25 |
+
<uses-permission android:name="android.permission.INTERNET" />
|
26 |
+
<uses-permission android:name="android.permission.CAMERA" />
|
27 |
+
<uses-permission android:name="android.permission.VIBRATE" />
|
28 |
+
|
29 |
+
<!-- أذونات SMS (للمستقبل) -->
|
30 |
+
<uses-permission android:name="android.permission.READ_SMS" />
|
31 |
+
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
32 |
+
|
33 |
+
<!-- أذونات البصمة -->
|
34 |
+
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
35 |
+
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
36 |
+
```
|
37 |
+
|
38 |
+
### **4. تحسين التطبيق للجوال**
|
39 |
+
- ✅ إضافة **Capacitor Core** للتفاعل مع ميزات الجهاز
|
40 |
+
- ✅ إعداد **SplashScreen** (شاشة البداية)
|
41 |
+
- ✅ تكوين **StatusBar** (شريط الحالة)
|
42 |
+
- ✅ إعداد **LocalNotifications** (الإشعارات المحلية)
|
43 |
+
|
44 |
+
### **5. إنشاء ملفات التعليمات**
|
45 |
+
- ✅ **APK_BUILD_GUIDE.md** - دليل بناء مفصل
|
46 |
+
- ✅ **QUICK_APK_BUILD.bat** - ملف بناء سريع
|
47 |
+
- ✅ **INSTALL_ANDROID_SDK.bat** - تثبيت Android SDK
|
48 |
+
- ✅ **APK_README.md** - دليل المستخدم النهائي
|
49 |
+
|
50 |
+
---
|
51 |
+
|
52 |
+
## 📱 **معلومات التطبيق النهائي**
|
53 |
+
|
54 |
+
| المعلومة | القيمة |
|
55 |
+
|---------|--------|
|
56 |
+
| **اسم التطبيق** | محفظتي الموحدة |
|
57 |
+
| **Package ID** | com.almada.unifiedwallet |
|
58 |
+
| **الإصدار** | 1.0.0 |
|
59 |
+
| **التقنية** | Capacitor + HTML/CSS/JS |
|
60 |
+
| **الحد الأدنى للأندرويد** | API 24 (Android 7.0) |
|
61 |
+
| **حجم APK المتوقع** | ~15-20 MB |
|
62 |
+
|
63 |
+
---
|
64 |
+
|
65 |
+
## 🔧 **الحالة الحالية**
|
66 |
+
|
67 |
+
### **جاهز للبناء:**
|
68 |
+
- ✅ جميع الملفات معدة
|
69 |
+
- ✅ التبعيات مثبتة
|
70 |
+
- ✅ Java JDK متوفر
|
71 |
+
- ✅ مشروع الأندرويد جاهز
|
72 |
+
|
73 |
+
### **المطلوب لإكمال البناء:**
|
74 |
+
- ❌ **Android SDK** (يحتاج تثبيت)
|
75 |
+
- ❌ تشغيل أمر البناء: `gradlew assembleDebug`
|
76 |
+
|
77 |
+
---
|
78 |
+
|
79 |
+
## 🚀 **طرق إكمال البناء**
|
80 |
+
|
81 |
+
### **الطريقة الأسهل:**
|
82 |
+
1. تثبيت [Android Studio](https://developer.android.com/studio)
|
83 |
+
2. فتح مجلد `android` في Android Studio
|
84 |
+
3. **Build > Build APK**
|
85 |
+
|
86 |
+
### **الطريقة السريعة:**
|
87 |
+
1. تشغيل `INSTALL_ANDROID_SDK.bat`
|
88 |
+
2. تشغيل `QUICK_APK_BUILD.bat`
|
89 |
+
|
90 |
+
### **الطريقة اليدوية:**
|
91 |
+
```bash
|
92 |
+
# تثبيت Android SDK
|
93 |
+
# ثم:
|
94 |
+
cd android
|
95 |
+
gradlew assembleDebug
|
96 |
+
```
|
97 |
+
|
98 |
+
---
|
99 |
+
|
100 |
+
## 📂 **هيكل المشروع النهائي**
|
101 |
+
|
102 |
+
```
|
103 |
+
almada/
|
104 |
+
├── 📱 android/ # مشروع الأندرويد الكامل
|
105 |
+
│ ├── app/ # تطبيق الأندرويد
|
106 |
+
│ ├── gradle/ # إعدادات Gradle
|
107 |
+
│ └── build.gradle # ملف البناء
|
108 |
+
├── 🌐 www/ # ملفات التطبيق
|
109 |
+
│ ├── index.html # الصفحة الرئيسية
|
110 |
+
│ ├── styles.css # التصميمات
|
111 |
+
│ ├── app.js # المنطق الرئيسي
|
112 |
+
│ ├── auth.js # نظام المصادقة
|
113 |
+
│ ├── wallets.js # إدارة المحافظ
|
114 |
+
│ └── notifications.js # الإشعارات
|
115 |
+
├── 📄 capacitor.config.ts # إعدادات Capacitor
|
116 |
+
├── 📦 package.json # تبعيات المشروع
|
117 |
+
├── 🔨 QUICK_APK_BUILD.bat # بناء سريع
|
118 |
+
├── ⚙️ INSTALL_ANDROID_SDK.bat # تثبيت SDK
|
119 |
+
├── 📖 APK_BUILD_GUIDE.md # دليل البناء المفصل
|
120 |
+
├── 📋 APK_README.md # دليل المستخدم
|
121 |
+
└── 📊 PROJECT_SUMMARY.md # هذا الملف
|
122 |
+
```
|
123 |
+
|
124 |
+
---
|
125 |
+
|
126 |
+
## 🎯 **الميزات المتاحة في التطبيق**
|
127 |
+
|
128 |
+
### **الأساسية:**
|
129 |
+
- 🔐 تس��يل دخول آمن (رقم هاتف + PIN)
|
130 |
+
- 💳 عرض 6 محافظ يمنية
|
131 |
+
- 🏠 واجهة رئيسية موحدة
|
132 |
+
- 🌙 دعم الوضع الليلي
|
133 |
+
|
134 |
+
### **التقنية:**
|
135 |
+
- 📱 واجهة أصلية للأندرويد
|
136 |
+
- 🔄 عمل بدون إنترنت
|
137 |
+
- 💾 حفظ البيانات محلياً
|
138 |
+
- 🎨 رسوم متحركة سلسة
|
139 |
+
|
140 |
+
### **الأمان:**
|
141 |
+
- 🛡️ تشفير البيانات
|
142 |
+
- 🔒 حماية PIN
|
143 |
+
- ⏰ انتهاء الجلسات
|
144 |
+
- 🚫 حماية من المحاولات المتكررة
|
145 |
+
|
146 |
+
---
|
147 |
+
|
148 |
+
## 🔮 **الميزات المستقبلية (جاهزة للتطوير)**
|
149 |
+
|
150 |
+
### **قراءة SMS:**
|
151 |
+
- 📨 استخراج أرصدة المحافظ من الرسائل
|
152 |
+
- 🔄 تحديث الأرصدة تلقائياً
|
153 |
+
- 📊 تحليل أنماط الإنفاق
|
154 |
+
|
155 |
+
### **المصادقة البيومترية:**
|
156 |
+
- 👆 بصمة الإصبع
|
157 |
+
- 👁️ التعرف على الوجه
|
158 |
+
- 🗣️ التعرف على الصوت
|
159 |
+
|
160 |
+
### **الإشعارات الذكية:**
|
161 |
+
- 💰 تنبيهات الرصيد المنخفض
|
162 |
+
- 💸 تنبيهات الإنفاق الزائد
|
163 |
+
- 📅 تذكير دفع الفواتير
|
164 |
+
|
165 |
+
---
|
166 |
+
|
167 |
+
## 💰 **نموذج الربح المقترح**
|
168 |
+
|
169 |
+
### **الإعلانات:**
|
170 |
+
- 📺 إعلانات مستهدفة بناءً على أنماط الإنفاق
|
171 |
+
- 🏪 شراكات مع المتاجر والخدمات
|
172 |
+
- 💳 عروض خاصة للمحافظ
|
173 |
+
|
174 |
+
### **الاشتراكات:**
|
175 |
+
- 🆓 **مجاني:** الميزات الأساسية
|
176 |
+
- 💎 **مميز:** تحليل متقدم + بدون إعلانات
|
177 |
+
- 🏢 **تجاري:** ميزات للشركات
|
178 |
+
|
179 |
+
### **العمولات:**
|
180 |
+
- 🤝 شراكات مع مقدمي الخدمات
|
181 |
+
- 🎁 كاش باك من المشتريات
|
182 |
+
- 💰 عمولات التحويلات
|
183 |
+
|
184 |
+
---
|
185 |
+
|
186 |
+
## 📈 **التوقعات**
|
187 |
+
|
188 |
+
### **السنة الأولى:**
|
189 |
+
- 👥 **10,000 مستخدم** نشط
|
190 |
+
- 💰 **400,000 ر.ي** دخل متوقع
|
191 |
+
- 📱 **50,000 تحميل** من Google Play
|
192 |
+
|
193 |
+
### **خطة النمو:**
|
194 |
+
1. **الشهر 1-3:** إطلاق وتسويق أولي
|
195 |
+
2. **الشهر 4-6:** إضافة ميزات SMS
|
196 |
+
3. **الشهر 7-9:** تطوير الذكاء الاصطناعي
|
197 |
+
4. **الشهر 10-12:** توسع إقليمي
|
198 |
+
|
199 |
+
---
|
200 |
+
|
201 |
+
## 🎊 **الخلاصة**
|
202 |
+
|
203 |
+
### **تم إنجازه:**
|
204 |
+
✅ **تطبيق ويب** محول إلى **تطبيق أندرويد**
|
205 |
+
✅ **جميع الملفات** جاهزة للبناء
|
206 |
+
✅ **التعليمات** مفصلة وواضحة
|
207 |
+
✅ **الأذونات** معدة للميزات المستقبلية
|
208 |
+
|
209 |
+
### **الخطوة التالية:**
|
210 |
+
🔨 **بناء APK** باستخدام إحدى الطرق المذكورة
|
211 |
+
|
212 |
+
### **النتيجة المتوقعة:**
|
213 |
+
📱 **تطبيق أندرويد** جاهز للتثبيت والاستخدام
|
214 |
+
|
215 |
+
---
|
216 |
+
|
217 |
+
**🎉 مبروك! مشروعك جاهز للانطلاق إلى العالم!**
|
218 |
+
|
219 |
+
*تطبيق محفظتي الموحدة - المدى للخدمات البرمجية التسويقية والإعلانية*
|
QUICK_APK_BUILD.bat
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
echo ========================================
|
3 |
+
echo محفظتي الموحدة - بناء APK سريع
|
4 |
+
echo ========================================
|
5 |
+
echo.
|
6 |
+
|
7 |
+
echo [1/5] التحقق من Java...
|
8 |
+
java -version
|
9 |
+
if %errorlevel% neq 0 (
|
10 |
+
echo ❌ Java غير مثبت! يرجى تثبيت Java JDK 11+
|
11 |
+
pause
|
12 |
+
exit /b 1
|
13 |
+
)
|
14 |
+
echo ✅ Java متوفر
|
15 |
+
|
16 |
+
echo.
|
17 |
+
echo [2/5] التحقق من Android SDK...
|
18 |
+
if not exist "%ANDROID_HOME%" (
|
19 |
+
echo ❌ Android SDK غير مثبت!
|
20 |
+
echo يرجى تثبيت Android Studio أو SDK Tools
|
21 |
+
echo أو تعيين متغير ANDROID_HOME
|
22 |
+
pause
|
23 |
+
exit /b 1
|
24 |
+
)
|
25 |
+
echo ✅ Android SDK متوفر
|
26 |
+
|
27 |
+
echo.
|
28 |
+
echo [3/5] نسخ ملفات التطبيق...
|
29 |
+
if not exist "www" mkdir www
|
30 |
+
copy index.html www\ >nul 2>&1
|
31 |
+
copy styles.css www\ >nul 2>&1
|
32 |
+
copy app.js www\ >nul 2>&1
|
33 |
+
copy auth.js www\ >nul 2>&1
|
34 |
+
copy wallets.js www\ >nul 2>&1
|
35 |
+
copy notifications.js www\ >nul 2>&1
|
36 |
+
copy demo.html www\ >nul 2>&1
|
37 |
+
copy src\manifest.json www\ >nul 2>&1
|
38 |
+
echo ✅ تم نسخ الملفات
|
39 |
+
|
40 |
+
echo.
|
41 |
+
echo [4/5] مزامنة Capacitor...
|
42 |
+
call cap sync android
|
43 |
+
if %errorlevel% neq 0 (
|
44 |
+
echo ❌ فشل في مزامنة Capacitor
|
45 |
+
pause
|
46 |
+
exit /b 1
|
47 |
+
)
|
48 |
+
echo ✅ تم مزامنة Capacitor
|
49 |
+
|
50 |
+
echo.
|
51 |
+
echo [5/5] بناء APK...
|
52 |
+
cd android
|
53 |
+
call gradlew assembleDebug
|
54 |
+
if %errorlevel% neq 0 (
|
55 |
+
echo ❌ فشل في بناء APK
|
56 |
+
echo يرجى مراجعة الأخطاء أعلاه
|
57 |
+
pause
|
58 |
+
exit /b 1
|
59 |
+
)
|
60 |
+
|
61 |
+
echo.
|
62 |
+
echo ========================================
|
63 |
+
echo 🎉 تم بناء APK بنجاح!
|
64 |
+
echo ========================================
|
65 |
+
echo.
|
66 |
+
echo 📱 ملف APK متوفر في:
|
67 |
+
echo android\app\build\outputs\apk\debug\app-debug.apk
|
68 |
+
echo.
|
69 |
+
echo 📋 الخطوات التالية:
|
70 |
+
echo 1. نسخ APK إلى الهاتف
|
71 |
+
echo 2. تفعيل "مصادر غير معروفة" في الإعدادات
|
72 |
+
echo 3. تثبيت APK
|
73 |
+
echo 4. اختبار التطبيق
|
74 |
+
echo.
|
75 |
+
echo 🔑 بيانات التجربة:
|
76 |
+
echo رقم الهاتف: 777123456
|
77 |
+
echo رمز PIN: 1234
|
78 |
+
echo.
|
79 |
+
pause
|
QUICK_START.md
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# البدء السريع - محفظتي الموحدة
|
2 |
+
|
3 |
+
## 🚀 تشغيل التطبيق في 5 دقائق
|
4 |
+
|
5 |
+
### المتطلبات الأساسية
|
6 |
+
- Node.js (الإصدار 18+)
|
7 |
+
- npm أو yarn
|
8 |
+
|
9 |
+
### خطوات سريعة
|
10 |
+
|
11 |
+
#### 1. تثبيت الأدوات
|
12 |
+
```bash
|
13 |
+
npm install -g @ionic/cli @angular/cli @capacitor/cli
|
14 |
+
```
|
15 |
+
|
16 |
+
#### 2. إعداد المشروع
|
17 |
+
```bash
|
18 |
+
cd almada
|
19 |
+
npm install
|
20 |
+
```
|
21 |
+
|
22 |
+
#### 3. تشغيل التطبيق
|
23 |
+
```bash
|
24 |
+
ionic serve
|
25 |
+
```
|
26 |
+
|
27 |
+
#### 4. فتح المتصفح
|
28 |
+
انتقل إلى: http://localhost:8100
|
29 |
+
|
30 |
+
### 📱 بناء تطبيق الجوال
|
31 |
+
|
32 |
+
#### للأندرويد:
|
33 |
+
```bash
|
34 |
+
ionic build --prod
|
35 |
+
ionic cap add android
|
36 |
+
ionic cap open android
|
37 |
+
```
|
38 |
+
|
39 |
+
#### لـ iOS:
|
40 |
+
```bash
|
41 |
+
ionic build --prod
|
42 |
+
ionic cap add ios
|
43 |
+
ionic cap open ios
|
44 |
+
```
|
45 |
+
|
46 |
+
## 🔑 بيانات التجربة
|
47 |
+
|
48 |
+
- **رقم الهاتف**: 777123456
|
49 |
+
- **رمز PIN**: 1234
|
50 |
+
|
51 |
+
## 📋 الميزات المتاحة
|
52 |
+
|
53 |
+
✅ تسجيل دخول آمن
|
54 |
+
✅ عرض المحافظ والأرصدة
|
55 |
+
✅ تحويل الأموال
|
56 |
+
✅ إشعارات فورية
|
57 |
+
✅ مصادقة بيومترية
|
58 |
+
✅ واجهة عربية كاملة
|
59 |
+
|
60 |
+
## 🛠️ استكشاف الأخطاء
|
61 |
+
|
62 |
+
### مشكلة: خطأ في npm install
|
63 |
+
```bash
|
64 |
+
npm cache clean --force
|
65 |
+
rm -rf node_modules
|
66 |
+
npm install
|
67 |
+
```
|
68 |
+
|
69 |
+
### مشكلة: خطأ في ionic serve
|
70 |
+
```bash
|
71 |
+
ionic repair
|
72 |
+
ionic serve --verbose
|
73 |
+
```
|
74 |
+
|
75 |
+
### مشكلة: خطأ في Capacitor
|
76 |
+
```bash
|
77 |
+
npx cap sync
|
78 |
+
npx cap doctor
|
79 |
+
```
|
80 |
+
|
81 |
+
## 📚 روابط مفيدة
|
82 |
+
|
83 |
+
- [تعليمات البناء الكاملة](BUILD_INSTRUCTIONS.md)
|
84 |
+
- [وثائق Ionic](https://ionicframework.com/docs)
|
85 |
+
- [وثائق Angular](https://angular.io/docs)
|
86 |
+
- [وثائق Capacitor](https://capacitorjs.com/docs)
|
87 |
+
|
88 |
+
## 💡 نصائح
|
89 |
+
|
90 |
+
1. **للتطوير السريع**: استخدم `ionic serve --lab`
|
91 |
+
2. **لاختبار الجوال**: استخدم `ionic cap run android --livereload`
|
92 |
+
3. **للتصحيح**: افتح Developer Tools في المتصفح
|
93 |
+
4. **للأداء**: استخدم `ionic build --prod` للإنتاج
|
94 |
+
|
95 |
+
## 🎯 الخطوات التالية
|
96 |
+
|
97 |
+
1. جرب تسجيل الدخول
|
98 |
+
2. استكشف المحافظ المختلفة
|
99 |
+
3. جرب التحويلات
|
100 |
+
4. اختبر الإشعارات
|
101 |
+
5. جرب المصادقة البيومترية (في التطبيق الأصلي)
|
102 |
+
|
103 |
+
---
|
104 |
+
|
105 |
+
**🎉 مبروك! تطبيقك جاهز للاستخدام**
|
106 |
+
|
107 |
+
للمساعدة أو الاستفسارات، راجع الوثائق الكاملة أو تواصل مع فريق التطوير.
|
README.md
CHANGED
@@ -1,25 +1,262 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
-
#
|
|
|
20 |
|
21 |
-
|
|
|
|
|
22 |
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
-
|
|
|
1 |
+
# محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية الموحد
|
2 |
+
|
3 |
+
## نظرة عامة
|
4 |
+
|
5 |
+
**محفظتي الموحدة** هو تطبيق جوال متطور مبني بتقنية Ionic/Angular يجمع جميع المحافظ الإلكترونية اليمنية في واجهة موحدة، مما يتيح للمستخدمين إدارة جميع محافظهم من مكان واحد باستخدام رقم هاتف موحد.
|
6 |
+
|
7 |
+
## 📱 **التطبيق متاح الآن كـ:**
|
8 |
+
- **تطبيق أندرويد** (APK)
|
9 |
+
- **تطبيق iOS** (IPA)
|
10 |
+
- **تطبيق ويب** (PWA)
|
11 |
+
- **تطبيق سطح المكتب** (عبر Electron)
|
12 |
+
|
13 |
+
## المحافظ المدعومة
|
14 |
+
|
15 |
+
التطبيق يدعم المحافظ الإلكترونية اليمنية التالية:
|
16 |
+
|
17 |
+
1. **جوالي (Jawali)** - من WeCash YE
|
18 |
+
2. **ONE Cash** - المحفظة الرقمية الأولى في اليمن
|
19 |
+
3. **Cash** - من Tamkeen Financial
|
20 |
+
4. **Jaib Digital Wallet** - من AHD Financial
|
21 |
+
5. **mFloos** - من Alkuraimi Islamic Microfinance Bank
|
22 |
+
6. **Mobile Money Wallet** - من CAC Bank
|
23 |
+
|
24 |
+
## الميزات الرئيسية
|
25 |
+
|
26 |
+
### 🔐 نظام أمان متقدم
|
27 |
+
- تسجيل دخول برقم الهاتف ورمز PIN
|
28 |
+
- مصادقة بيومترية (بصمة الإصبع)
|
29 |
+
- حماية من المحاولات المتكررة
|
30 |
+
- جلسات آمنة مع انتهاء صلاحية تلقائي
|
31 |
+
|
32 |
+
### 💸 إدارة المحافظ
|
33 |
+
- عرض جميع المحافظ في واجهة موحدة
|
34 |
+
- عرض الأرصدة الإجمالية والفردية
|
35 |
+
- تحديث الأرصدة في الوقت الفعلي
|
36 |
+
- إخفاء/إظهار الأرصدة للخصوصية
|
37 |
+
|
38 |
+
### 🔄 التحويلات والمدفوعات
|
39 |
+
- تحويل الأموال بين المحافظ المختلفة
|
40 |
+
- دفع الفواتير (كهرباء، مياه، إنترنت)
|
41 |
+
- شحن أرصدة الهواتف
|
42 |
+
- مسح رموز QR للدفع
|
43 |
+
|
44 |
+
### 📱 واجهة مستخدم عصرية
|
45 |
+
- تصميم متجاوب يعمل على جميع الأجهزة
|
46 |
+
- واجهة باللغة العربية مع دعم RTL
|
47 |
+
- رسوم متحركة سلسة
|
48 |
+
- تجربة مستخدم بديهية
|
49 |
+
|
50 |
+
### 🔔 نظام إشعارات متطور
|
51 |
+
- إشعارات فورية للمعاملات
|
52 |
+
- تنبيهات أمنية
|
53 |
+
- إشعارات النظام
|
54 |
+
- إدارة الإشعارات المقروءة وغير المقروءة
|
55 |
+
|
56 |
+
## التقنيات المستخدمة
|
57 |
+
|
58 |
+
### Frontend Framework
|
59 |
+
- **Ionic 7** - إطار عمل التطبيقات الهجينة
|
60 |
+
- **Angular 17** - إطار عمل الواجهة الأمامية
|
61 |
+
- **TypeScript** - لغة البرمجة الأساسية
|
62 |
+
- **SCSS** - معالج CSS المتقدم
|
63 |
+
|
64 |
+
### Mobile Development
|
65 |
+
- **Capacitor 5** - منصة التطبيقات الأصلية
|
66 |
+
- **Cordova Plugins** - الوصول لميزات الجهاز
|
67 |
+
- **PWA** - تطبيق ويب تقدمي
|
68 |
+
|
69 |
+
### Backend & Storage
|
70 |
+
- **Ionic Storage** - تخزين البيانات المحلية
|
71 |
+
- **RxJS** - إدارة البيانات التفاعلية
|
72 |
+
- **HTTP Client** - التواصل مع APIs
|
73 |
+
|
74 |
+
### UI/UX
|
75 |
+
- **Ionic Components** - مكونات واجهة المستخدم
|
76 |
+
- **Ionicons** - مكتبة الأيقونات
|
77 |
+
- **Google Fonts** - خط Tajawal العربي
|
78 |
+
- **CSS Animations** - الرسوم المتحركة
|
79 |
+
|
80 |
+
### Development Tools
|
81 |
+
- **Angular CLI** - أدوات التطوير
|
82 |
+
- **Capacitor CLI** - أدوات البناء للجوال
|
83 |
+
- **ESLint** - فحص جودة الكود
|
84 |
+
- **Prettier** - تنسيق الكود
|
85 |
+
|
86 |
+
## هيكل المشروع
|
87 |
+
|
88 |
+
```
|
89 |
+
almada/
|
90 |
+
├── src/ # مجلد المصدر الرئيسي
|
91 |
+
│ ├── app/ # تطبيق Angular
|
92 |
+
│ │ ├── pages/ # صفحات التطبيق
|
93 |
+
│ │ │ ├── login/ # صفحة تسجيل الدخول
|
94 |
+
│ │ │ ├── home/ # الصفحة الرئيسية
|
95 |
+
│ │ │ ├── wallets/ # صفحة المحافظ
|
96 |
+
│ │ │ ├── transfer/ # صفحة التحويلات
|
97 |
+
│ │ │ └── ... # باقي الصفحات
|
98 |
+
│ │ ├── services/ # الخدمات
|
99 |
+
│ │ │ ├── auth.service.ts # خدمة المصادقة
|
100 |
+
│ │ │ ├── wallet.service.ts # خدمة المحافظ
|
101 |
+
│ │ │ └── ... # باقي الخدمات
|
102 |
+
│ │ ├── guards/ # حراس الحماية
|
103 |
+
│ │ └── components/ # المكونات المشتركة
|
104 |
+
│ ├── assets/ # الملفات الثابتة
|
105 |
+
│ ├── theme/ # ملفات الثيم
|
106 |
+
│ └── environments/ # إعدادات البيئة
|
107 |
+
├── android/ # مشروع الأندرويد
|
108 |
+
├── ios/ # مشروع iOS
|
109 |
+
├── capacitor.config.ts # إعدادات Capacitor
|
110 |
+
├── ionic.config.json # إعدادات Ionic
|
111 |
+
├── angular.json # إعدادات Angular
|
112 |
+
├── package.json # تبعيات المشروع
|
113 |
+
├── BUILD_INSTRUCTIONS.md # تعليمات البناء
|
114 |
+
└── README.md # هذا الملف
|
115 |
+
```
|
116 |
+
|
117 |
+
## كيفية التشغيل
|
118 |
+
|
119 |
+
### 1. تشغيل للتطوير
|
120 |
+
|
121 |
+
```bash
|
122 |
+
# استنساخ المشروع
|
123 |
+
git clone [repository-url]
|
124 |
+
cd almada
|
125 |
+
|
126 |
+
# تثبيت التبعيات
|
127 |
+
npm install
|
128 |
+
|
129 |
+
# تشغيل خادم التطوير
|
130 |
+
ionic serve
|
131 |
+
|
132 |
+
# فتح المتصفح على
|
133 |
+
http://localhost:8100
|
134 |
+
```
|
135 |
+
|
136 |
+
### 2. بناء التطبيق للجوال
|
137 |
+
|
138 |
+
```bash
|
139 |
+
# بناء المشروع
|
140 |
+
ionic build --prod
|
141 |
+
|
142 |
+
# إضافة منصة الأندرويد
|
143 |
+
ionic cap add android
|
144 |
+
|
145 |
+
# إضافة منصة iOS
|
146 |
+
ionic cap add ios
|
147 |
|
148 |
+
# بناء APK للأندرويد
|
149 |
+
ionic cap build android
|
150 |
|
151 |
+
# بناء IPA لـ iOS
|
152 |
+
ionic cap build ios
|
153 |
+
```
|
154 |
|
155 |
+
### 3. تشغيل على الأجهزة
|
156 |
+
|
157 |
+
```bash
|
158 |
+
# تشغيل على الأندرويد
|
159 |
+
ionic cap run android
|
160 |
+
|
161 |
+
# تشغيل على iOS
|
162 |
+
ionic cap run ios
|
163 |
+
|
164 |
+
# تشغيل في المتصفح مع إعادة التحميل
|
165 |
+
ionic serve --lab
|
166 |
+
```
|
167 |
+
|
168 |
+
راجع ملف [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) للتفاصيل الكاملة.
|
169 |
+
|
170 |
+
## بيانات التجربة
|
171 |
+
|
172 |
+
للاختبار، يمكن استخدام البيانات التالية:
|
173 |
+
|
174 |
+
- **رقم الهاتف**: أي رقم يمني صحيح (9 أرقام)
|
175 |
+
- **رمز PIN**: أي رمز من 4-6 أرقام
|
176 |
+
- **مثال**: 777123456 / 1234
|
177 |
+
|
178 |
+
## الاستخدام
|
179 |
+
|
180 |
+
### تسجيل الدخول
|
181 |
+
1. أدخل رقم الهاتف (9 أرقام)
|
182 |
+
2. أدخل رمز PIN (4-6 أرقام)
|
183 |
+
3. أو استخدم المصادقة البيومترية
|
184 |
+
|
185 |
+
### إدارة المحافظ
|
186 |
+
- عرض جميع المحافظ والأرصدة
|
187 |
+
- تحديث الأرصدة
|
188 |
+
- إخفاء/إظهار الأرصدة
|
189 |
+
|
190 |
+
### التحويلات
|
191 |
+
1. اختر المحفظة المرسلة
|
192 |
+
2. أدخل تفاصيل التحويل
|
193 |
+
3. أكد بـ PIN
|
194 |
+
|
195 |
+
### الإشعارات
|
196 |
+
- عرض الإشعارات من الأيقونة في الأعلى
|
197 |
+
- وضع علامة مقروء
|
198 |
+
- حذف الإشعارات
|
199 |
+
|
200 |
+
## الأمان
|
201 |
+
|
202 |
+
التطبيق يتضمن عدة طبقات أمان:
|
203 |
+
|
204 |
+
- **تشفير البيانات**: جميع البيانات الحساسة مشفرة
|
205 |
+
- **جلسات آمنة**: انتهاء صلاحية تلقائي للجلسات
|
206 |
+
- **حماية من الهجمات**: حماية من المحاولات المتكررة
|
207 |
+
- **مصادقة متعددة**: PIN + بصمة
|
208 |
+
- **تخزين آمن**: استخدام Local Storage بشكل آمن
|
209 |
+
|
210 |
+
## ملاحظات مهمة
|
211 |
+
|
212 |
+
⚠️ **هذا تطبيق تجريبي لأغراض العرض فقط**
|
213 |
+
|
214 |
+
- جميع البيانات والمعاملات محاكاة
|
215 |
+
- لا يؤثر على الحسابات الحقيقية
|
216 |
+
- البيانات محفوظة محلياً في المتصفح
|
217 |
+
- يتطلب متصفح حديث للمصادقة البيومترية
|
218 |
+
|
219 |
+
## التطوير المستقبلي
|
220 |
+
|
221 |
+
### الميزات المخطط لها
|
222 |
+
- [ ] دعم المزيد من المحافظ
|
223 |
+
- [ ] تطبيق جوال أصلي
|
224 |
+
- [ ] تكامل مع APIs الحقيقية
|
225 |
+
- [ ] نظام إحصائيات متقدم
|
226 |
+
- [ ] دعم العملات المتعددة
|
227 |
+
- [ ] نظام النسخ الاحتياطي
|
228 |
+
|
229 |
+
### التحسينات التقنية
|
230 |
+
- [ ] PWA (Progressive Web App)
|
231 |
+
- [ ] وضع عدم الاتصال
|
232 |
+
- [ ] تحسين الأداء
|
233 |
+
- [ ] اختبارات تلقائية
|
234 |
+
- [ ] CI/CD Pipeline
|
235 |
+
|
236 |
+
## المساهمة
|
237 |
+
|
238 |
+
نرحب بالمساهمات! يرجى:
|
239 |
+
|
240 |
+
1. Fork المشروع
|
241 |
+
2. إنشاء branch للميزة الجديدة
|
242 |
+
3. Commit التغييرات
|
243 |
+
4. Push إلى Branch
|
244 |
+
5. فتح Pull Request
|
245 |
+
|
246 |
+
## الترخيص
|
247 |
+
|
248 |
+
هذا المشروع مرخص تحت رخصة MIT - انظر ملف [LICENSE](LICENSE) للتفاصيل.
|
249 |
+
|
250 |
+
## التواصل
|
251 |
+
|
252 |
+
**المدى للخدمات البرمجية التسويقية والإعلانية**
|
253 |
+
- المدير العام: المهندس/ محمد المرتضى
|
254 |
+
- © 2025 جميع الحقوق محفوظة
|
255 |
+
|
256 |
+
## الدعم
|
257 |
+
|
258 |
+
للدعم التقني أو الاستفسارات، يرجى فتح issue في المستودع أو التواصل مع فريق التطوير.
|
259 |
+
|
260 |
+
---
|
261 |
|
262 |
+
**شكراً لاستخدام محفظتي الموحدة! 🚀**
|
README_GITHUB.md
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📱 محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية
|
2 |
+
|
3 |
+
[](https://github.com/USERNAME/almada-unified-wallet/actions/workflows/build-apk.yml)
|
4 |
+
[](https://github.com/USERNAME/almada-unified-wallet/releases/latest)
|
5 |
+
|
6 |
+
## 🎯 **نظرة عامة**
|
7 |
+
|
8 |
+
**محفظتي الموحدة** هو تطبيق أندرويد متطور يجمع جميع المحافظ الإلكترونية اليمنية في واجهة موحدة، مما يتيح للمستخدمين إدارة جميع محافظهم من مكان واحد.
|
9 |
+
|
10 |
+
## 📱 **تحميل التطبيق**
|
11 |
+
|
12 |
+
### 🚀 **أحدث إصدار:**
|
13 |
+
[](https://github.com/USERNAME/almada-unified-wallet/releases/latest/download/app-debug.apk)
|
14 |
+
|
15 |
+
### 🔄 **البناء التلقائي:**
|
16 |
+
- يتم بناء APK تلقائياً عند كل تحديث
|
17 |
+
- متوفر في قسم [Actions](https://github.com/USERNAME/almada-unified-wallet/actions)
|
18 |
+
- تحميل مباشر من [Releases](https://github.com/USERNAME/almada-unified-wallet/releases)
|
19 |
+
|
20 |
+
## 🎯 **الميزات**
|
21 |
+
|
22 |
+
### ✅ **المتاحة حالياً:**
|
23 |
+
- 🔐 تسجيل دخول آمن برقم الهاتف + PIN
|
24 |
+
- 💳 عرض 6 محافظ يمنية رئيسية:
|
25 |
+
- جوالي (Jawali)
|
26 |
+
- ONE Cash
|
27 |
+
- Cash
|
28 |
+
- Aman
|
29 |
+
- Tadawul
|
30 |
+
- Al-Kuraimi
|
31 |
+
- 🏠 واجهة موحدة لجميع المحافظ
|
32 |
+
- 🌙 دعم الوضع الليلي
|
33 |
+
- 🔄 واجهة عربية كاملة (RTL)
|
34 |
+
- 📱 تصميم متجاوب لجميع أحجام الشاشات
|
35 |
+
|
36 |
+
### 🔄 **قيد التطوير:**
|
37 |
+
- 📨 قراءة رسائل SMS لاستخراج الأرصدة
|
38 |
+
- 👆 مصادقة بيومترية (بصمة/وجه)
|
39 |
+
- 🔔 إشعارات ذكية ومخصصة
|
40 |
+
- 📊 تحليل أنماط الإنفاق والتوفير
|
41 |
+
- 💰 اقتراحات التوفير الذكية
|
42 |
+
|
43 |
+
## 🔑 **بيانات التجربة**
|
44 |
+
|
45 |
+
للدخول إلى التطبيق:
|
46 |
+
- **رقم الهاتف:** `777123456`
|
47 |
+
- **رمز PIN:** `1234`
|
48 |
+
|
49 |
+
## 🛠️ **التقنيات المستخدمة**
|
50 |
+
|
51 |
+
- **Frontend:** HTML5, CSS3, JavaScript (ES6+)
|
52 |
+
- **Mobile Framework:** Capacitor 5
|
53 |
+
- **Build System:** Gradle
|
54 |
+
- **CI/CD:** GitHub Actions
|
55 |
+
- **Platform:** Android (API 24+)
|
56 |
+
|
57 |
+
## 📲 **التثبيت**
|
58 |
+
|
59 |
+
### **من GitHub Releases:**
|
60 |
+
1. اذهب إلى [Releases](https://github.com/USERNAME/almada-unified-wallet/releases)
|
61 |
+
2. حمل أحدث ملف APK
|
62 |
+
3. في الهاتف: فعل "مصادر غير معروفة"
|
63 |
+
4. ثبت التطبيق
|
64 |
+
|
65 |
+
### **من GitHub Actions:**
|
66 |
+
1. اذهب إلى [Actions](https://github.com/USERNAME/almada-unified-wallet/actions)
|
67 |
+
2. اختر آخر build ناجح
|
68 |
+
3. حمل APK من Artifacts
|
69 |
+
|
70 |
+
## 🏗️ **البناء المحلي**
|
71 |
+
|
72 |
+
```bash
|
73 |
+
# استنساخ المشروع
|
74 |
+
git clone https://github.com/USERNAME/almada-unified-wallet.git
|
75 |
+
cd almada-unified-wallet
|
76 |
+
|
77 |
+
# تثبيت التبعيات
|
78 |
+
npm install
|
79 |
+
|
80 |
+
# بناء للأندرويد
|
81 |
+
npx cap sync android
|
82 |
+
cd android
|
83 |
+
./gradlew assembleDebug
|
84 |
+
```
|
85 |
+
|
86 |
+
## 📊 **معلومات التطبيق**
|
87 |
+
|
88 |
+
| المعلومة | القيمة |
|
89 |
+
|---------|--------|
|
90 |
+
| **اسم التطبيق** | محفظتي الموحدة |
|
91 |
+
| **Package ID** | com.almada.unifiedwallet |
|
92 |
+
| **الإصدار** | 1.0.0 |
|
93 |
+
| **حجم APK** | ~15-20 MB |
|
94 |
+
| **الحد الأدنى** | Android 7.0 (API 24) |
|
95 |
+
| **اللغة** | العربية (RTL) |
|
96 |
+
|
97 |
+
## 🤝 **المساهمة**
|
98 |
+
|
99 |
+
نرحب بالمساهمات! يرجى:
|
100 |
+
1. Fork المشروع
|
101 |
+
2. إنشاء branch للميزة الجديدة
|
102 |
+
3. Commit التغييرات
|
103 |
+
4. Push إلى Branch
|
104 |
+
5. فتح Pull Request
|
105 |
+
|
106 |
+
## 📄 **الترخيص**
|
107 |
+
|
108 |
+
هذا المشروع مرخص تحت رخصة MIT - راجع ملف [LICENSE](LICENSE) للتفاصيل.
|
109 |
+
|
110 |
+
## 📞 **التواصل**
|
111 |
+
|
112 |
+
- **الشركة:** المدى للخدمات البرمجية التسويقية والإعلانية
|
113 |
+
- **الموقع:** https://almada.com
|
114 |
+
- **البريد الإلكتروني:** info@almada.com
|
115 |
+
|
116 |
+
## 🎉 **شكر خاص**
|
117 |
+
|
118 |
+
شكر خاص لجميع مطوري المحافظ الإلكترونية اليمنية والمجتمع التقني اليمني.
|
119 |
+
|
120 |
+
---
|
121 |
+
|
122 |
+
**🚀 مبروك! تطبيقك متاح للعالم!**
|
123 |
+
|
124 |
+
*Made with ❤️ in Yemen*
|
STEP_BY_STEP_APK.md
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📱 دليل بناء APK خطوة بخطوة - مصور
|
2 |
+
|
3 |
+
## 🎯 **الهدف:** الحصول على ملف APK جاهز للتثبيت في 15 دقيقة
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## 🚀 **الطريقة الأسرع: ملف BAT التلقائي**
|
8 |
+
|
9 |
+
### **الخطوة 1: تشغيل الملف التلقائي**
|
10 |
+
1. **اذهب إلى مجلد المشروع:** `E:\almada`
|
11 |
+
2. **اضغط مرتين على:** `BUILD_APK_SIMPLE.bat`
|
12 |
+
3. **انتظر اكتمال العملية** (5-10 دقائق)
|
13 |
+
|
14 |
+
### **إذا نجحت العملية:**
|
15 |
+
- ✅ ستفتح نافذة تحتوي على ملف APK
|
16 |
+
- ✅ الملف سيكون: `app-debug.apk`
|
17 |
+
- ✅ انسخه إلى هاتفك وثبته
|
18 |
+
|
19 |
+
### **إذا فشلت العملية:**
|
20 |
+
- ❌ انتقل للطريقة الثانية أدناه
|
21 |
+
|
22 |
+
---
|
23 |
+
|
24 |
+
## 🏗️ **الطريقة الثانية: Android Studio (مضمونة 100%)**
|
25 |
+
|
26 |
+
### **الخطوة 1: فتح Android Studio**
|
27 |
+
```
|
28 |
+
1. افتح Android Studio
|
29 |
+
2. اختر "Open an Existing Project"
|
30 |
+
3. انتقل إلى: E:\almada\android
|
31 |
+
4. اختر مجلد android واضغط OK
|
32 |
+
```
|
33 |
+
|
34 |
+
### **الخطوة 2: انتظار التحميل**
|
35 |
+
```
|
36 |
+
⏳ انتظر رسالة: "Gradle sync finished"
|
37 |
+
📦 قد يستغرق 5-10 دقائق في المرة الأولى
|
38 |
+
🌐 تأكد من اتصال الإنترنت
|
39 |
+
```
|
40 |
+
|
41 |
+
### **الخطوة 3: بناء APK**
|
42 |
+
```
|
43 |
+
1. في القائمة العلوية: Build
|
44 |
+
2. اختر: Build Bundle(s) / APK(s)
|
45 |
+
3. اختر: Build APK(s)
|
46 |
+
4. انتظر رسالة: "APK(s) generated successfully"
|
47 |
+
```
|
48 |
+
|
49 |
+
### **الخطوة 4: العثور على APK**
|
50 |
+
```
|
51 |
+
📂 المسار: E:\almada\android\app\build\outputs\apk\debug\
|
52 |
+
📱 الملف: app-debug.apk
|
53 |
+
💾 الحجم: ~15-20 MB
|
54 |
+
```
|
55 |
+
|
56 |
+
---
|
57 |
+
|
58 |
+
## 📲 **تثبيت التطبيق على الهاتف**
|
59 |
+
|
60 |
+
### **الطريقة 1: نسخ مباشر**
|
61 |
+
1. **انسخ ملف APK** إلى هاتفك (عبر USB أو البلوتوث)
|
62 |
+
2. **في الهاتف:** Settings > Security > Install from Unknown Sources ✅
|
63 |
+
3. **اضغط على ملف APK** واختر Install
|
64 |
+
4. **انتظر اكتمال التثبيت**
|
65 |
+
|
66 |
+
### **الطريقة 2: ADB (للمتقدمين)**
|
67 |
+
```bash
|
68 |
+
# وصل الهاتف بـ USB وفعل USB Debugging
|
69 |
+
adb install app-debug.apk
|
70 |
+
```
|
71 |
+
|
72 |
+
---
|
73 |
+
|
74 |
+
## 🔑 **اختبار التطبيق**
|
75 |
+
|
76 |
+
بعد التثبيت:
|
77 |
+
1. **افتح التطبيق:** "محفظتي الموحدة"
|
78 |
+
2. **أدخل البيانات:**
|
79 |
+
- رقم الهاتف: `777123456`
|
80 |
+
- رمز PIN: `1234`
|
81 |
+
3. **استكشف الميزات:**
|
82 |
+
- عرض المحافظ
|
83 |
+
- التنقل بين الصفحات
|
84 |
+
- اختبار الواجهة العربية
|
85 |
+
|
86 |
+
---
|
87 |
+
|
88 |
+
## 🆘 **حل المشاكل الشائعة**
|
89 |
+
|
90 |
+
### **مشكلة: "App not installed"**
|
91 |
+
```
|
92 |
+
الحل:
|
93 |
+
1. تأكد من تفعيل "Install from Unknown Sources"
|
94 |
+
2. احذف أي إصدار قديم من التطبيق
|
95 |
+
3. أعد تشغيل الهاتف وحاول مرة أخرى
|
96 |
+
```
|
97 |
+
|
98 |
+
### **مشكلة: "Parse error"**
|
99 |
+
```
|
100 |
+
الحل:
|
101 |
+
1. تأكد من أن ملف APK غير تالف
|
102 |
+
2. أعد تحميل/نسخ الملف
|
103 |
+
3. تأكد من توافق إصدار الأندرويد (7.0+)
|
104 |
+
```
|
105 |
+
|
106 |
+
### **مشكلة: "Gradle build failed"**
|
107 |
+
```
|
108 |
+
الحل:
|
109 |
+
1. تأكد من اتصال الإنترنت
|
110 |
+
2. في Android Studio: Build > Clean Project
|
111 |
+
3. ثم: Build > Rebuild Project
|
112 |
+
```
|
113 |
+
|
114 |
+
---
|
115 |
+
|
116 |
+
## 📊 **معلومات التطبيق النهائي**
|
117 |
+
|
118 |
+
| المعلومة | القيمة |
|
119 |
+
|---------|--------|
|
120 |
+
| **اسم التطبيق** | محفظتي الموحدة |
|
121 |
+
| **Package Name** | com.almada.unifiedwallet |
|
122 |
+
| **الإصدار** | 1.0.0 |
|
123 |
+
| **حجم APK** | ~15-20 MB |
|
124 |
+
| **الحد الأدنى** | Android 7.0 (API 24) |
|
125 |
+
| **اللغة** | العربية (RTL) |
|
126 |
+
| **النوع** | Debug APK |
|
127 |
+
|
128 |
+
---
|
129 |
+
|
130 |
+
## 🎯 **الميزات المتاحة**
|
131 |
+
|
132 |
+
### **الحالية:**
|
133 |
+
- ✅ تسجيل دخول آمن
|
134 |
+
- ✅ عرض 6 محافظ يمنية
|
135 |
+
- ✅ واجهة عربية كاملة
|
136 |
+
- ✅ تصميم متجاوب
|
137 |
+
- ✅ حفظ البيانات محلياً
|
138 |
+
|
139 |
+
### **المستقبلية:**
|
140 |
+
- 🔄 قراءة رسائل SMS
|
141 |
+
- 🔄 مصادقة بيومترية
|
142 |
+
- 🔄 إشعارات ذكية
|
143 |
+
- 🔄 تحليل الإنفاق
|
144 |
+
|
145 |
+
---
|
146 |
+
|
147 |
+
## 💡 **نصائح مهمة**
|
148 |
+
|
149 |
+
### **للبناء الناجح:**
|
150 |
+
- 🌐 تأكد من **اتصال الإنترنت** القوي
|
151 |
+
- ⏰ **لا تقاطع** عملية التحميل
|
152 |
+
- 💾 تأكد من **مساحة كافية** (5+ GB)
|
153 |
+
|
154 |
+
### **للاختبار:**
|
155 |
+
- 📱 اختبر على **أجهزة مختلفة**
|
156 |
+
- 🔄 اختبر **جميع الميزات**
|
157 |
+
- 📊 راقب **الأداء**
|
158 |
+
|
159 |
+
### **للمشاركة:**
|
160 |
+
- 📤 يمكنك **مشاركة APK** مع الآخرين
|
161 |
+
- 🔒 هذا **إصدار تجريبي** (Debug)
|
162 |
+
- 🏪 للنشر في Google Play تحتاج **إصدار Release**
|
163 |
+
|
164 |
+
---
|
165 |
+
|
166 |
+
## 🎉 **تهانينا!**
|
167 |
+
|
168 |
+
عند اكتمال هذه الخطوات، ستحصل على:
|
169 |
+
- 📱 **تطبيق أندرويد** كامل وجاهز
|
170 |
+
- 💼 **محفظة موحدة** لجميع المحافظ اليمنية
|
171 |
+
- �� **واجهة احترافية** باللغة العربية
|
172 |
+
- 🔒 **نظام أمان** متقدم
|
173 |
+
|
174 |
+
**🚀 مبروك! تطبيقك جاهز للعالم!**
|
175 |
+
|
176 |
+
---
|
177 |
+
|
178 |
+
## 📞 **تحتاج مساعدة؟**
|
179 |
+
|
180 |
+
إذا واجهت أي مشكلة:
|
181 |
+
1. راجع قسم "حل المشاكل" أعلاه
|
182 |
+
2. تأكد من اتباع الخطوات بالترتيب
|
183 |
+
3. تواصل للحصول على المساعدة
|
184 |
+
|
185 |
+
**💪 لا تستسلم - النجاح قريب!**
|
Windows
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
اختر: "Command line tools only"
|
angular.json
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
3 |
+
"version": 1,
|
4 |
+
"newProjectRoot": "projects",
|
5 |
+
"projects": {
|
6 |
+
"unified-wallet-app": {
|
7 |
+
"projectType": "application",
|
8 |
+
"schematics": {
|
9 |
+
"@ionic/angular-toolkit:component": {
|
10 |
+
"styleext": "scss"
|
11 |
+
},
|
12 |
+
"@ionic/angular-toolkit:page": {
|
13 |
+
"styleext": "scss"
|
14 |
+
}
|
15 |
+
},
|
16 |
+
"root": "",
|
17 |
+
"sourceRoot": "src",
|
18 |
+
"prefix": "app",
|
19 |
+
"architect": {
|
20 |
+
"build": {
|
21 |
+
"builder": "@angular-devkit/build-angular:browser",
|
22 |
+
"options": {
|
23 |
+
"outputPath": "dist/unified-wallet-app",
|
24 |
+
"index": "src/index.html",
|
25 |
+
"main": "src/main.ts",
|
26 |
+
"polyfills": [
|
27 |
+
"zone.js"
|
28 |
+
],
|
29 |
+
"tsConfig": "tsconfig.app.json",
|
30 |
+
"inlineStyleLanguage": "scss",
|
31 |
+
"assets": [
|
32 |
+
{
|
33 |
+
"glob": "**/*",
|
34 |
+
"input": "src/assets",
|
35 |
+
"output": "assets"
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"glob": "**/*.svg",
|
39 |
+
"input": "node_modules/ionicons/dist/ionicons/svg",
|
40 |
+
"output": "./svg"
|
41 |
+
}
|
42 |
+
],
|
43 |
+
"styles": [
|
44 |
+
"src/theme/variables.scss",
|
45 |
+
"src/global.scss"
|
46 |
+
],
|
47 |
+
"scripts": []
|
48 |
+
},
|
49 |
+
"configurations": {
|
50 |
+
"production": {
|
51 |
+
"budgets": [
|
52 |
+
{
|
53 |
+
"type": "initial",
|
54 |
+
"maximumWarning": "2mb",
|
55 |
+
"maximumError": "5mb"
|
56 |
+
},
|
57 |
+
{
|
58 |
+
"type": "anyComponentStyle",
|
59 |
+
"maximumWarning": "2kb",
|
60 |
+
"maximumError": "4kb"
|
61 |
+
}
|
62 |
+
],
|
63 |
+
"outputHashing": "all"
|
64 |
+
},
|
65 |
+
"development": {
|
66 |
+
"buildOptimizer": false,
|
67 |
+
"optimization": false,
|
68 |
+
"vendorChunk": true,
|
69 |
+
"extractLicenses": false,
|
70 |
+
"sourceMap": true,
|
71 |
+
"namedChunks": true
|
72 |
+
}
|
73 |
+
},
|
74 |
+
"defaultConfiguration": "production"
|
75 |
+
},
|
76 |
+
"serve": {
|
77 |
+
"builder": "@angular-devkit/build-angular:dev-server",
|
78 |
+
"configurations": {
|
79 |
+
"production": {
|
80 |
+
"buildTarget": "unified-wallet-app:build:production"
|
81 |
+
},
|
82 |
+
"development": {
|
83 |
+
"buildTarget": "unified-wallet-app:build:development"
|
84 |
+
}
|
85 |
+
},
|
86 |
+
"defaultConfiguration": "development"
|
87 |
+
},
|
88 |
+
"extract-i18n": {
|
89 |
+
"builder": "@angular-devkit/build-angular:extract-i18n",
|
90 |
+
"options": {
|
91 |
+
"buildTarget": "unified-wallet-app:build"
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"test": {
|
95 |
+
"builder": "@angular-devkit/build-angular:karma",
|
96 |
+
"options": {
|
97 |
+
"polyfills": [
|
98 |
+
"zone.js",
|
99 |
+
"zone.js/testing"
|
100 |
+
],
|
101 |
+
"tsConfig": "tsconfig.spec.json",
|
102 |
+
"inlineStyleLanguage": "scss",
|
103 |
+
"assets": [
|
104 |
+
{
|
105 |
+
"glob": "**/*",
|
106 |
+
"input": "src/assets",
|
107 |
+
"output": "assets"
|
108 |
+
},
|
109 |
+
{
|
110 |
+
"glob": "**/*.svg",
|
111 |
+
"input": "node_modules/ionicons/dist/ionicons/svg",
|
112 |
+
"output": "./svg"
|
113 |
+
}
|
114 |
+
],
|
115 |
+
"styles": [
|
116 |
+
"src/theme/variables.scss",
|
117 |
+
"src/global.scss"
|
118 |
+
],
|
119 |
+
"scripts": []
|
120 |
+
}
|
121 |
+
},
|
122 |
+
"lint": {
|
123 |
+
"builder": "@angular-eslint/builder:lint",
|
124 |
+
"options": {
|
125 |
+
"lintFilePatterns": [
|
126 |
+
"src/**/*.ts",
|
127 |
+
"src/**/*.html"
|
128 |
+
]
|
129 |
+
}
|
130 |
+
}
|
131 |
+
}
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"cli": {
|
135 |
+
"schematicCollections": [
|
136 |
+
"@ionic/angular-toolkit"
|
137 |
+
]
|
138 |
+
},
|
139 |
+
"schematics": {
|
140 |
+
"@ionic/angular-toolkit:component": {
|
141 |
+
"styleext": "scss"
|
142 |
+
},
|
143 |
+
"@ionic/angular-toolkit:page": {
|
144 |
+
"styleext": "scss"
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
app.js
ADDED
@@ -0,0 +1,780 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// التطبيق الرئيسي - محفظتي الموحدة
|
2 |
+
class UnifiedWalletApp {
|
3 |
+
constructor() {
|
4 |
+
this.currentUser = null;
|
5 |
+
this.currentScreen = 'login';
|
6 |
+
this.wallets = [];
|
7 |
+
this.transactions = [];
|
8 |
+
this.isBalanceHidden = false;
|
9 |
+
|
10 |
+
// تهيئة المدراء
|
11 |
+
this.authManager = new AuthenticationManager();
|
12 |
+
this.walletManager = new WalletManager();
|
13 |
+
this.notificationManager = new NotificationManager();
|
14 |
+
|
15 |
+
this.init();
|
16 |
+
}
|
17 |
+
|
18 |
+
// تهيئة التطبيق
|
19 |
+
init() {
|
20 |
+
this.setupEventListeners();
|
21 |
+
this.loadUserData();
|
22 |
+
this.showLoadingScreen();
|
23 |
+
|
24 |
+
// محاكاة تحميل البيانات
|
25 |
+
setTimeout(() => {
|
26 |
+
this.hideLoadingScreen();
|
27 |
+
this.checkAuthStatus();
|
28 |
+
}, 2000);
|
29 |
+
}
|
30 |
+
|
31 |
+
// إعداد مستمعي الأحداث
|
32 |
+
setupEventListeners() {
|
33 |
+
// تسجيل الدخول
|
34 |
+
document.getElementById('login-btn').addEventListener('click', () => this.handleLogin());
|
35 |
+
document.getElementById('biometric-login').addEventListener('click', () => this.handleBiometricLogin());
|
36 |
+
document.getElementById('register-link').addEventListener('click', (e) => {
|
37 |
+
e.preventDefault();
|
38 |
+
this.showRegisterModal();
|
39 |
+
});
|
40 |
+
|
41 |
+
// الشاشة الرئيسية
|
42 |
+
document.getElementById('refresh-balance').addEventListener('click', () => this.refreshBalances());
|
43 |
+
document.getElementById('hide-balance').addEventListener('click', () => this.toggleBalanceVisibility());
|
44 |
+
|
45 |
+
// الإجراءات السريعة
|
46 |
+
document.getElementById('transfer-btn').addEventListener('click', () => this.showTransferScreen());
|
47 |
+
document.getElementById('pay-bills-btn').addEventListener('click', () => this.showBillsScreen());
|
48 |
+
document.getElementById('recharge-btn').addEventListener('click', () => this.showRechargeScreen());
|
49 |
+
document.getElementById('qr-scan-btn').addEventListener('click', () => this.startQRScan());
|
50 |
+
|
51 |
+
// شريط التنقل
|
52 |
+
document.querySelectorAll('.nav-item').forEach(item => {
|
53 |
+
item.addEventListener('click', () => {
|
54 |
+
const screen = item.dataset.screen;
|
55 |
+
this.switchScreen(screen);
|
56 |
+
});
|
57 |
+
});
|
58 |
+
|
59 |
+
// أزرار الإعدادات والإشعارات
|
60 |
+
document.getElementById('settings-btn').addEventListener('click', () => this.showSettings());
|
61 |
+
document.getElementById('notifications-btn').addEventListener('click', () => this.showNotifications());
|
62 |
+
|
63 |
+
// أزرار الإدارة
|
64 |
+
document.getElementById('manage-wallets').addEventListener('click', () => this.showWalletManagement());
|
65 |
+
document.getElementById('view-all-transactions').addEventListener('click', () => this.showAllTransactions());
|
66 |
+
|
67 |
+
// أزرار الرجوع
|
68 |
+
document.getElementById('transfer-back').addEventListener('click', () => this.switchScreen('main'));
|
69 |
+
|
70 |
+
// شاشة التحويل
|
71 |
+
this.setupTransferScreen();
|
72 |
+
|
73 |
+
// إدخال رقم الهاتف
|
74 |
+
document.getElementById('phone-number').addEventListener('input', this.formatPhoneNumber);
|
75 |
+
document.getElementById('pin-code').addEventListener('keypress', (e) => {
|
76 |
+
if (e.key === 'Enter') this.handleLogin();
|
77 |
+
});
|
78 |
+
}
|
79 |
+
|
80 |
+
// عرض شاشة التحميل
|
81 |
+
showLoadingScreen() {
|
82 |
+
document.getElementById('loading-screen').style.display = 'flex';
|
83 |
+
}
|
84 |
+
|
85 |
+
// إخفاء شاشة التحميل
|
86 |
+
hideLoadingScreen() {
|
87 |
+
document.getElementById('loading-screen').style.display = 'none';
|
88 |
+
}
|
89 |
+
|
90 |
+
// فحص حالة المصادقة
|
91 |
+
checkAuthStatus() {
|
92 |
+
const savedUser = localStorage.getItem('unifiedWallet_user');
|
93 |
+
if (savedUser) {
|
94 |
+
this.currentUser = JSON.parse(savedUser);
|
95 |
+
this.switchScreen('main');
|
96 |
+
this.loadUserWallets();
|
97 |
+
} else {
|
98 |
+
this.switchScreen('login');
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
// تسجيل الدخول
|
103 |
+
async handleLogin() {
|
104 |
+
const phoneNumber = document.getElementById('phone-number').value;
|
105 |
+
const pinCode = document.getElementById('pin-code').value;
|
106 |
+
|
107 |
+
if (!phoneNumber || !pinCode) {
|
108 |
+
this.showAlert('يرجى إدخال رقم الهاتف ورمز PIN', 'error');
|
109 |
+
return;
|
110 |
+
}
|
111 |
+
|
112 |
+
this.showLoading('جاري تسجيل الدخول...');
|
113 |
+
|
114 |
+
try {
|
115 |
+
const user = await this.authManager.loginWithPin(phoneNumber, pinCode);
|
116 |
+
this.currentUser = user;
|
117 |
+
|
118 |
+
this.hideLoading();
|
119 |
+
this.showAlert('تم تسجيل الدخول بنجاح', 'success');
|
120 |
+
this.switchScreen('main');
|
121 |
+
await this.loadUserWallets();
|
122 |
+
|
123 |
+
} catch (error) {
|
124 |
+
this.hideLoading();
|
125 |
+
this.showAlert(error.message, 'error');
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
// تسجيل الدخ��ل بالبصمة
|
130 |
+
async handleBiometricLogin() {
|
131 |
+
this.showLoading('جاري التحقق من البصمة...');
|
132 |
+
|
133 |
+
try {
|
134 |
+
const user = await this.authManager.loginWithBiometric();
|
135 |
+
this.currentUser = user;
|
136 |
+
|
137 |
+
this.hideLoading();
|
138 |
+
this.showAlert('تم تسجيل الدخول بالبصمة بنجاح', 'success');
|
139 |
+
this.switchScreen('main');
|
140 |
+
await this.loadUserWallets();
|
141 |
+
|
142 |
+
} catch (error) {
|
143 |
+
this.hideLoading();
|
144 |
+
this.showAlert(error.message, 'error');
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
// تحميل محافظ المستخدم
|
149 |
+
async loadUserWallets() {
|
150 |
+
this.showLoading('جاري تحميل المحافظ...');
|
151 |
+
|
152 |
+
try {
|
153 |
+
// الحصول على محافظ المستخدم من مدير المحافظ
|
154 |
+
this.wallets = this.walletManager.getUserWallets();
|
155 |
+
|
156 |
+
// إذا لم توجد محافظ، إنشاء محافظ تجريبية
|
157 |
+
if (this.wallets.length === 0) {
|
158 |
+
await this.createDemoWallets();
|
159 |
+
}
|
160 |
+
|
161 |
+
// تحديث أرصدة المحافظ
|
162 |
+
await this.walletManager.updateAllBalances();
|
163 |
+
this.wallets = this.walletManager.getUserWallets();
|
164 |
+
|
165 |
+
this.loadRecentTransactions();
|
166 |
+
this.updateUI();
|
167 |
+
this.hideLoading();
|
168 |
+
|
169 |
+
} catch (error) {
|
170 |
+
this.hideLoading();
|
171 |
+
this.showAlert('فشل في تحميل المحافظ', 'error');
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
// إنشاء محافظ تجريبية
|
176 |
+
async createDemoWallets() {
|
177 |
+
const demoWallets = [
|
178 |
+
{ id: 'jawali', accountNumber: '777123456', pin: '1234' },
|
179 |
+
{ id: 'onecash', accountNumber: '777234567', pin: '1234' },
|
180 |
+
{ id: 'cash', accountNumber: '777345678', pin: '1234' },
|
181 |
+
{ id: 'jaib', accountNumber: '777456789', pin: '1234' },
|
182 |
+
{ id: 'mfloos', accountNumber: '777567890', pin: '1234' },
|
183 |
+
{ id: 'mobilemoney', accountNumber: '777678901', pin: '1234' }
|
184 |
+
];
|
185 |
+
|
186 |
+
for (const wallet of demoWallets) {
|
187 |
+
try {
|
188 |
+
await this.walletManager.addWallet(wallet.id, wallet.accountNumber, wallet.pin);
|
189 |
+
} catch (error) {
|
190 |
+
console.warn(`فشل في إضافة محفظة ${wallet.id}:`, error.message);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
// تحميل المعاملات الأخيرة
|
196 |
+
loadRecentTransactions() {
|
197 |
+
this.transactions = [
|
198 |
+
{
|
199 |
+
id: 1,
|
200 |
+
type: 'send',
|
201 |
+
title: 'تحويل إلى أحمد محمد',
|
202 |
+
wallet: 'جوالي',
|
203 |
+
amount: -500,
|
204 |
+
time: '10:30 ص',
|
205 |
+
date: new Date().toISOString()
|
206 |
+
},
|
207 |
+
{
|
208 |
+
id: 2,
|
209 |
+
type: 'receive',
|
210 |
+
title: 'استلام من سارة أحمد',
|
211 |
+
wallet: 'ONE Cash',
|
212 |
+
amount: 1200,
|
213 |
+
time: '09:15 ص',
|
214 |
+
date: new Date().toISOString()
|
215 |
+
},
|
216 |
+
{
|
217 |
+
id: 3,
|
218 |
+
type: 'bill',
|
219 |
+
title: 'فاتورة كهرباء',
|
220 |
+
wallet: 'Cash',
|
221 |
+
amount: -350,
|
222 |
+
time: 'أمس',
|
223 |
+
date: new Date(Date.now() - 86400000).toISOString()
|
224 |
+
},
|
225 |
+
{
|
226 |
+
id: 4,
|
227 |
+
type: 'send',
|
228 |
+
title: 'شحن رصيد',
|
229 |
+
wallet: 'Jaib',
|
230 |
+
amount: -100,
|
231 |
+
time: 'أمس',
|
232 |
+
date: new Date(Date.now() - 86400000).toISOString()
|
233 |
+
}
|
234 |
+
];
|
235 |
+
}
|
236 |
+
|
237 |
+
// تحديث واجهة المستخدم
|
238 |
+
updateUI() {
|
239 |
+
this.updateUserInfo();
|
240 |
+
this.updateTotalBalance();
|
241 |
+
this.renderWallets();
|
242 |
+
this.renderTransactions();
|
243 |
+
}
|
244 |
+
|
245 |
+
// تحديث معلومات المستخدم
|
246 |
+
updateUserInfo() {
|
247 |
+
if (this.currentUser) {
|
248 |
+
document.getElementById('user-name').textContent = `مرحباً ${this.currentUser.name}`;
|
249 |
+
document.getElementById('user-phone').textContent = this.currentUser.phone;
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
// تحديث الرصيد الإجمالي
|
254 |
+
updateTotalBalance() {
|
255 |
+
const total = this.wallets.reduce((sum, wallet) => sum + wallet.balance, 0);
|
256 |
+
const balanceElement = document.getElementById('total-balance');
|
257 |
+
|
258 |
+
if (this.isBalanceHidden) {
|
259 |
+
balanceElement.textContent = '••••• ر.ي';
|
260 |
+
} else {
|
261 |
+
balanceElement.textContent = `${total.toLocaleString()} ر.ي`;
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
// عرض المحافظ
|
266 |
+
renderWallets() {
|
267 |
+
const walletsList = document.getElementById('wallets-list');
|
268 |
+
walletsList.innerHTML = '';
|
269 |
+
|
270 |
+
this.wallets.forEach(wallet => {
|
271 |
+
const walletCard = this.createWalletCard(wallet);
|
272 |
+
walletsList.appendChild(walletCard);
|
273 |
+
});
|
274 |
+
}
|
275 |
+
|
276 |
+
// إنشاء بطاقة محفظة
|
277 |
+
createWalletCard(wallet) {
|
278 |
+
const card = document.createElement('div');
|
279 |
+
card.className = 'wallet-card';
|
280 |
+
card.onclick = () => this.openWalletDetails(wallet);
|
281 |
+
|
282 |
+
const balanceDisplay = this.isBalanceHidden ?
|
283 |
+
'•••••' : wallet.balance.toLocaleString();
|
284 |
+
|
285 |
+
card.innerHTML = `
|
286 |
+
<div class="wallet-info">
|
287 |
+
<div class="wallet-icon">
|
288 |
+
<img src="${wallet.icon}" alt="${wallet.name}" onerror="this.style.display='none'">
|
289 |
+
</div>
|
290 |
+
<div class="wallet-details">
|
291 |
+
<h4>${wallet.name}</h4>
|
292 |
+
<p>${wallet.provider}</p>
|
293 |
+
</div>
|
294 |
+
</div>
|
295 |
+
<div class="wallet-balance">
|
296 |
+
<div class="amount">${balanceDisplay}</div>
|
297 |
+
<div class="currency">ر.ي</div>
|
298 |
+
</div>
|
299 |
+
`;
|
300 |
+
|
301 |
+
return card;
|
302 |
+
}
|
303 |
+
|
304 |
+
// عرض المعاملات
|
305 |
+
renderTransactions() {
|
306 |
+
const transactionsList = document.getElementById('transactions-list');
|
307 |
+
transactionsList.innerHTML = '';
|
308 |
+
|
309 |
+
this.transactions.slice(0, 5).forEach(transaction => {
|
310 |
+
const transactionItem = this.createTransactionItem(transaction);
|
311 |
+
transactionsList.appendChild(transactionItem);
|
312 |
+
});
|
313 |
+
}
|
314 |
+
|
315 |
+
// إنشاء عنصر معاملة
|
316 |
+
createTransactionItem(transaction) {
|
317 |
+
const item = document.createElement('div');
|
318 |
+
item.className = 'transaction-item';
|
319 |
+
|
320 |
+
const iconClass = transaction.type === 'send' ? 'fas fa-arrow-up' :
|
321 |
+
transaction.type === 'receive' ? 'fas fa-arrow-down' :
|
322 |
+
'fas fa-file-invoice-dollar';
|
323 |
+
|
324 |
+
const amountClass = transaction.amount > 0 ? 'positive' : 'negative';
|
325 |
+
const amountSign = transaction.amount > 0 ? '+' : '';
|
326 |
+
|
327 |
+
item.innerHTML = `
|
328 |
+
<div class="transaction-info">
|
329 |
+
<div class="transaction-icon ${transaction.type}">
|
330 |
+
<i class="${iconClass}"></i>
|
331 |
+
</div>
|
332 |
+
<div class="transaction-details">
|
333 |
+
<h5>${transaction.title}</h5>
|
334 |
+
<p>${transaction.wallet}</p>
|
335 |
+
</div>
|
336 |
+
</div>
|
337 |
+
<div class="transaction-amount ${amountClass}">
|
338 |
+
<div class="amount">${amountSign}${Math.abs(transaction.amount).toLocaleString()} ر.ي</div>
|
339 |
+
<div class="time">${transaction.time}</div>
|
340 |
+
</div>
|
341 |
+
`;
|
342 |
+
|
343 |
+
return item;
|
344 |
+
}
|
345 |
+
|
346 |
+
// محاكاة استدعاء API
|
347 |
+
simulateAPICall(delay = 1000) {
|
348 |
+
return new Promise((resolve) => {
|
349 |
+
setTimeout(resolve, delay);
|
350 |
+
});
|
351 |
+
}
|
352 |
+
|
353 |
+
// عرض رسالة تنبيه
|
354 |
+
showAlert(message, type = 'info') {
|
355 |
+
this.notificationManager.showToast(message, type);
|
356 |
+
}
|
357 |
+
|
358 |
+
// عرض شاشة التحميل
|
359 |
+
showLoading(message = 'جاري التحميل...') {
|
360 |
+
this.loadingModal = this.notificationManager.showLoading(message);
|
361 |
+
}
|
362 |
+
|
363 |
+
// إخفاء شاشة التحميل
|
364 |
+
hideLoading() {
|
365 |
+
this.notificationManager.hideLoading();
|
366 |
+
}
|
367 |
+
|
368 |
+
// تبديل الشاشات
|
369 |
+
switchScreen(screenName) {
|
370 |
+
document.querySelectorAll('.screen').forEach(screen => {
|
371 |
+
screen.classList.remove('active');
|
372 |
+
});
|
373 |
+
|
374 |
+
document.getElementById(`${screenName}-screen`).classList.add('active');
|
375 |
+
|
376 |
+
// تحديث شريط التنقل
|
377 |
+
document.querySelectorAll('.nav-item').forEach(item => {
|
378 |
+
item.classList.remove('active');
|
379 |
+
});
|
380 |
+
|
381 |
+
const activeNavItem = document.querySelector(`[data-screen="${screenName}"]`);
|
382 |
+
if (activeNavItem) {
|
383 |
+
activeNavItem.classList.add('active');
|
384 |
+
}
|
385 |
+
|
386 |
+
this.currentScreen = screenName;
|
387 |
+
}
|
388 |
+
|
389 |
+
// تنسيق رقم الهاتف
|
390 |
+
formatPhoneNumber(e) {
|
391 |
+
let value = e.target.value.replace(/\D/g, '');
|
392 |
+
if (value.length > 9) {
|
393 |
+
value = value.slice(0, 9);
|
394 |
+
}
|
395 |
+
e.target.value = value;
|
396 |
+
}
|
397 |
+
|
398 |
+
// تحديث الأرصدة
|
399 |
+
async refreshBalances() {
|
400 |
+
this.showLoading('جاري تحديث الأرصدة...');
|
401 |
+
await this.simulateAPICall(1500);
|
402 |
+
this.updateTotalBalance();
|
403 |
+
this.renderWallets();
|
404 |
+
this.hideLoading();
|
405 |
+
this.showAlert('تم تحديث الأرصدة بنجاح', 'success');
|
406 |
+
}
|
407 |
+
|
408 |
+
// إخفاء/إظهار الرصيد
|
409 |
+
toggleBalanceVisibility() {
|
410 |
+
this.isBalanceHidden = !this.isBalanceHidden;
|
411 |
+
const hideBtn = document.getElementById('hide-balance');
|
412 |
+
const icon = hideBtn.querySelector('i');
|
413 |
+
|
414 |
+
if (this.isBalanceHidden) {
|
415 |
+
icon.className = 'fas fa-eye';
|
416 |
+
hideBtn.innerHTML = '<i class="fas fa-eye"></i> إظهار';
|
417 |
+
} else {
|
418 |
+
icon.className = 'fas fa-eye-slash';
|
419 |
+
hideBtn.innerHTML = '<i class="fas fa-eye-slash"></i> إخفاء';
|
420 |
+
}
|
421 |
+
|
422 |
+
this.updateTotalBalance();
|
423 |
+
this.renderWallets();
|
424 |
+
}
|
425 |
+
|
426 |
+
// تحميل بيانات المستخدم
|
427 |
+
loadUserData() {
|
428 |
+
// تحميل الإعدادات المحفوظة
|
429 |
+
const savedSettings = localStorage.getItem('unifiedWallet_settings');
|
430 |
+
if (savedSettings) {
|
431 |
+
const settings = JSON.parse(savedSettings);
|
432 |
+
this.isBalanceHidden = settings.hideBalance || false;
|
433 |
+
}
|
434 |
+
}
|
435 |
+
|
436 |
+
// حفظ بيانات المستخدم
|
437 |
+
saveUserData() {
|
438 |
+
const settings = {
|
439 |
+
hideBalance: this.isBalanceHidden
|
440 |
+
};
|
441 |
+
localStorage.setItem('unifiedWallet_settings', JSON.stringify(settings));
|
442 |
+
}
|
443 |
+
|
444 |
+
// إعداد شاشة التحويل
|
445 |
+
setupTransferScreen() {
|
446 |
+
this.currentTransferStep = 1;
|
447 |
+
this.selectedSourceWallet = null;
|
448 |
+
this.transferData = {};
|
449 |
+
|
450 |
+
// أزرار التنقل
|
451 |
+
document.getElementById('transfer-next-btn').addEventListener('click', () => this.nextTransferStep());
|
452 |
+
document.getElementById('transfer-prev-btn').addEventListener('click', () => this.prevTransferStep());
|
453 |
+
document.getElementById('transfer-confirm-btn').addEventListener('click', () => this.confirmTransfer());
|
454 |
+
|
455 |
+
// اقتراحات المبلغ
|
456 |
+
document.querySelectorAll('.amount-btn').forEach(btn => {
|
457 |
+
btn.addEventListener('click', () => {
|
458 |
+
document.getElementById('transfer-amount').value = btn.dataset.amount;
|
459 |
+
});
|
460 |
+
});
|
461 |
+
}
|
462 |
+
|
463 |
+
// عرض شاشة التحويل
|
464 |
+
showTransferScreen() {
|
465 |
+
this.switchScreen('transfer');
|
466 |
+
this.resetTransferForm();
|
467 |
+
this.renderSourceWallets();
|
468 |
+
}
|
469 |
+
|
470 |
+
// إعادة تعيين نموذج التحويل
|
471 |
+
resetTransferForm() {
|
472 |
+
this.currentTransferStep = 1;
|
473 |
+
this.selectedSourceWallet = null;
|
474 |
+
this.transferData = {};
|
475 |
+
|
476 |
+
// إعادة تعيين الخطوات
|
477 |
+
this.updateTransferSteps();
|
478 |
+
|
479 |
+
// مسح النموذج
|
480 |
+
document.getElementById('destination-wallet').value = '';
|
481 |
+
document.getElementById('recipient-number').value = '';
|
482 |
+
document.getElementById('transfer-amount').value = '';
|
483 |
+
document.getElementById('transfer-note').value = '';
|
484 |
+
document.getElementById('transfer-pin').value = '';
|
485 |
+
}
|
486 |
+
|
487 |
+
// تحديث مؤشر الخطوات
|
488 |
+
updateTransferSteps() {
|
489 |
+
for (let i = 1; i <= 3; i++) {
|
490 |
+
const step = document.getElementById(`transfer-step-${i}`);
|
491 |
+
const content = document.getElementById(`transfer-step-content-${i}`);
|
492 |
+
|
493 |
+
if (i < this.currentTransferStep) {
|
494 |
+
step.classList.add('completed');
|
495 |
+
step.classList.remove('active');
|
496 |
+
} else if (i === this.currentTransferStep) {
|
497 |
+
step.classList.add('active');
|
498 |
+
step.classList.remove('completed');
|
499 |
+
} else {
|
500 |
+
step.classList.remove('active', 'completed');
|
501 |
+
}
|
502 |
+
|
503 |
+
content.classList.toggle('active', i === this.currentTransferStep);
|
504 |
+
}
|
505 |
+
|
506 |
+
// تحديث أزرار التنقل
|
507 |
+
const prevBtn = document.getElementById('transfer-prev-btn');
|
508 |
+
const nextBtn = document.getElementById('transfer-next-btn');
|
509 |
+
const confirmBtn = document.getElementById('transfer-confirm-btn');
|
510 |
+
|
511 |
+
prevBtn.style.display = this.currentTransferStep > 1 ? 'block' : 'none';
|
512 |
+
nextBtn.style.display = this.currentTransferStep < 3 ? 'block' : 'none';
|
513 |
+
confirmBtn.style.display = this.currentTransferStep === 3 ? 'block' : 'none';
|
514 |
+
}
|
515 |
+
|
516 |
+
// عرض المحافظ المصدر
|
517 |
+
renderSourceWallets() {
|
518 |
+
const container = document.getElementById('source-wallet-selection');
|
519 |
+
container.innerHTML = '';
|
520 |
+
|
521 |
+
this.wallets.forEach(wallet => {
|
522 |
+
const option = this.createWalletOption(wallet);
|
523 |
+
container.appendChild(option);
|
524 |
+
});
|
525 |
+
}
|
526 |
+
|
527 |
+
// إنشاء خيار محفظة
|
528 |
+
createWalletOption(wallet) {
|
529 |
+
const option = document.createElement('div');
|
530 |
+
option.className = 'wallet-option';
|
531 |
+
option.onclick = () => this.selectSourceWallet(wallet);
|
532 |
+
|
533 |
+
const balanceDisplay = this.isBalanceHidden ?
|
534 |
+
'•••••' : wallet.balance.toLocaleString();
|
535 |
+
|
536 |
+
option.innerHTML = `
|
537 |
+
<div class="wallet-option-info">
|
538 |
+
<div class="wallet-option-icon">
|
539 |
+
<img src="${wallet.icon}" alt="${wallet.name}" onerror="this.style.display='none'">
|
540 |
+
</div>
|
541 |
+
<div class="wallet-option-details">
|
542 |
+
<h4>${wallet.name}</h4>
|
543 |
+
<p>${wallet.provider}</p>
|
544 |
+
</div>
|
545 |
+
</div>
|
546 |
+
<div class="wallet-option-balance">
|
547 |
+
<div class="amount">${balanceDisplay}</div>
|
548 |
+
<div class="currency">ر.ي</div>
|
549 |
+
</div>
|
550 |
+
`;
|
551 |
+
|
552 |
+
return option;
|
553 |
+
}
|
554 |
+
|
555 |
+
// اختيار المحفظة المصدر
|
556 |
+
selectSourceWallet(wallet) {
|
557 |
+
this.selectedSourceWallet = wallet;
|
558 |
+
|
559 |
+
// تحديث التحديد البصري
|
560 |
+
document.querySelectorAll('.wallet-option').forEach(option => {
|
561 |
+
option.classList.remove('selected');
|
562 |
+
});
|
563 |
+
event.currentTarget.classList.add('selected');
|
564 |
+
|
565 |
+
this.transferData.sourceWallet = wallet;
|
566 |
+
}
|
567 |
+
|
568 |
+
// الخطوة التالية
|
569 |
+
nextTransferStep() {
|
570 |
+
if (this.validateCurrentStep()) {
|
571 |
+
this.currentTransferStep++;
|
572 |
+
this.updateTransferSteps();
|
573 |
+
|
574 |
+
if (this.currentTransferStep === 3) {
|
575 |
+
this.updateTransferSummary();
|
576 |
+
}
|
577 |
+
}
|
578 |
+
}
|
579 |
+
|
580 |
+
// الخطوة السابقة
|
581 |
+
prevTransferStep() {
|
582 |
+
this.currentTransferStep--;
|
583 |
+
this.updateTransferSteps();
|
584 |
+
}
|
585 |
+
|
586 |
+
// التحقق من صحة الخطوة الحالية
|
587 |
+
validateCurrentStep() {
|
588 |
+
switch (this.currentTransferStep) {
|
589 |
+
case 1:
|
590 |
+
if (!this.selectedSourceWallet) {
|
591 |
+
this.showAlert('يرجى اختيار المحفظة المرسلة', 'error');
|
592 |
+
return false;
|
593 |
+
}
|
594 |
+
return true;
|
595 |
+
|
596 |
+
case 2:
|
597 |
+
const destinationWallet = document.getElementById('destination-wallet').value;
|
598 |
+
const recipientNumber = document.getElementById('recipient-number').value;
|
599 |
+
const amount = parseFloat(document.getElementById('transfer-amount').value);
|
600 |
+
|
601 |
+
if (!destinationWallet) {
|
602 |
+
this.showAlert('يرجى اختيار المحفظة المستقبلة', 'error');
|
603 |
+
return false;
|
604 |
+
}
|
605 |
+
|
606 |
+
if (!recipientNumber || recipientNumber.length !== 9) {
|
607 |
+
this.showAlert('يرجى إدخال رقم المستقبل صحيح (9 أرقام)', 'error');
|
608 |
+
return false;
|
609 |
+
}
|
610 |
+
|
611 |
+
if (!amount || amount <= 0) {
|
612 |
+
this.showAlert('يرجى إدخال مبلغ صحيح', 'error');
|
613 |
+
return false;
|
614 |
+
}
|
615 |
+
|
616 |
+
if (amount > this.selectedSourceWallet.balance) {
|
617 |
+
this.showAlert('المبلغ أكبر من الرصيد المتاح', 'error');
|
618 |
+
return false;
|
619 |
+
}
|
620 |
+
|
621 |
+
// حفظ بيانات التحويل
|
622 |
+
this.transferData.destinationWallet = destinationWallet;
|
623 |
+
this.transferData.recipientNumber = recipientNumber;
|
624 |
+
this.transferData.amount = amount;
|
625 |
+
this.transferData.note = document.getElementById('transfer-note').value;
|
626 |
+
|
627 |
+
return true;
|
628 |
+
|
629 |
+
default:
|
630 |
+
return true;
|
631 |
+
}
|
632 |
+
}
|
633 |
+
|
634 |
+
// تحديث ملخص التحويل
|
635 |
+
updateTransferSummary() {
|
636 |
+
const sourceWalletInfo = this.walletManager.getWalletInfo(this.transferData.sourceWallet.id);
|
637 |
+
const destinationWalletInfo = this.walletManager.getWalletInfo(this.transferData.destinationWallet);
|
638 |
+
|
639 |
+
const fee = sourceWalletInfo.fees.transfer;
|
640 |
+
const total = this.transferData.amount + fee;
|
641 |
+
|
642 |
+
document.getElementById('summary-from-wallet').textContent = this.transferData.sourceWallet.name;
|
643 |
+
document.getElementById('summary-to-wallet').textContent = destinationWalletInfo.name;
|
644 |
+
document.getElementById('summary-recipient').textContent = `+967${this.transferData.recipientNumber}`;
|
645 |
+
document.getElementById('summary-amount').textContent = `${this.transferData.amount.toLocaleString()} ر.ي`;
|
646 |
+
document.getElementById('summary-fee').textContent = `${fee.toLocaleString()} ر.ي`;
|
647 |
+
document.getElementById('summary-total').textContent = `${total.toLocaleString()} ر.ي`;
|
648 |
+
}
|
649 |
+
|
650 |
+
// تأكيد التحويل
|
651 |
+
async confirmTransfer() {
|
652 |
+
const pin = document.getElementById('transfer-pin').value;
|
653 |
+
|
654 |
+
if (!pin) {
|
655 |
+
this.showAlert('يرجى إدخال رمز PIN', 'error');
|
656 |
+
return;
|
657 |
+
}
|
658 |
+
|
659 |
+
this.showLoading('جاري تنفيذ التحويل...');
|
660 |
+
|
661 |
+
try {
|
662 |
+
const result = await this.walletManager.executeTransfer(
|
663 |
+
this.transferData.sourceWallet.id,
|
664 |
+
this.transferData.destinationWallet,
|
665 |
+
this.transferData.amount,
|
666 |
+
this.transferData.recipientNumber,
|
667 |
+
pin
|
668 |
+
);
|
669 |
+
|
670 |
+
this.hideLoading();
|
671 |
+
this.showTransferSuccess(result);
|
672 |
+
|
673 |
+
// تحديث البيانات
|
674 |
+
await this.loadUserWallets();
|
675 |
+
|
676 |
+
} catch (error) {
|
677 |
+
this.hideLoading();
|
678 |
+
this.showAlert(error.message, 'error');
|
679 |
+
}
|
680 |
+
}
|
681 |
+
|
682 |
+
// عرض نجاح التحويل
|
683 |
+
showTransferSuccess(result) {
|
684 |
+
const message = `
|
685 |
+
تم التحويل بنجاح!
|
686 |
+
|
687 |
+
رقم المعاملة: ${result.transactionId}
|
688 |
+
المبلغ المحول: ${result.amount.toLocaleString()} ر.ي
|
689 |
+
الرسوم: ${result.fee.toLocaleString()} ر.ي
|
690 |
+
الرصيد المتبقي: ${result.newBalance.toLocaleString()} ر.ي
|
691 |
+
`;
|
692 |
+
|
693 |
+
this.showAlert(message, 'success');
|
694 |
+
this.switchScreen('main');
|
695 |
+
}
|
696 |
+
|
697 |
+
// الوظائف التي سيتم تطويرها لاحقاً
|
698 |
+
showBillsScreen() { console.log('عرض شاشة الفواتير'); }
|
699 |
+
showRechargeScreen() { console.log('عرض شاشة الشحن'); }
|
700 |
+
startQRScan() { console.log('بدء مسح QR'); }
|
701 |
+
showSettings() { console.log('عرض الإعدادات'); }
|
702 |
+
showNotifications() {
|
703 |
+
this.notificationManager.showNotificationsList();
|
704 |
+
}
|
705 |
+
showWalletManagement() { console.log('إدارة المحافظ'); }
|
706 |
+
showAllTransactions() { console.log('عرض جميع المعاملات'); }
|
707 |
+
openWalletDetails(wallet) { console.log('تفاصيل المحفظة:', wallet.name); }
|
708 |
+
showRegisterModal() {
|
709 |
+
this.notificationManager.showModal(
|
710 |
+
'إنشاء حساب جديد',
|
711 |
+
`
|
712 |
+
<div class="form-group">
|
713 |
+
<label>رقم الهاتف</label>
|
714 |
+
<div class="input-group">
|
715 |
+
<span class="input-prefix">+967</span>
|
716 |
+
<input type="tel" id="register-phone" placeholder="7xxxxxxxx" maxlength="9">
|
717 |
+
</div>
|
718 |
+
</div>
|
719 |
+
<div class="form-group">
|
720 |
+
<label>رمز PIN</label>
|
721 |
+
<input type="password" id="register-pin" placeholder="أدخل رمز PIN" maxlength="6">
|
722 |
+
</div>
|
723 |
+
<div class="form-group">
|
724 |
+
<label>تأكيد رمز PIN</label>
|
725 |
+
<input type="password" id="register-pin-confirm" placeholder="أعد إدخال رمز PIN" maxlength="6">
|
726 |
+
</div>
|
727 |
+
`,
|
728 |
+
[
|
729 |
+
{
|
730 |
+
text: 'إنشاء الحساب',
|
731 |
+
class: 'btn-primary',
|
732 |
+
action: () => this.handleRegister()
|
733 |
+
},
|
734 |
+
{
|
735 |
+
text: 'إلغاء',
|
736 |
+
class: 'btn-secondary'
|
737 |
+
}
|
738 |
+
]
|
739 |
+
);
|
740 |
+
}
|
741 |
+
|
742 |
+
// التعامل مع التسجيل
|
743 |
+
handleRegister() {
|
744 |
+
const phone = document.getElementById('register-phone').value;
|
745 |
+
const pin = document.getElementById('register-pin').value;
|
746 |
+
const pinConfirm = document.getElementById('register-pin-confirm').value;
|
747 |
+
|
748 |
+
if (!phone || !pin || !pinConfirm) {
|
749 |
+
this.showAlert('يرجى ملء جميع الحقول', 'error');
|
750 |
+
return;
|
751 |
+
}
|
752 |
+
|
753 |
+
if (pin !== pinConfirm) {
|
754 |
+
this.showAlert('رمز PIN غير متطابق', 'error');
|
755 |
+
return;
|
756 |
+
}
|
757 |
+
|
758 |
+
if (phone.length !== 9) {
|
759 |
+
this.showAlert('رقم الهاتف يجب أن يكون 9 أرقام', 'error');
|
760 |
+
return;
|
761 |
+
}
|
762 |
+
|
763 |
+
if (pin.length < 4) {
|
764 |
+
this.showAlert('رمز PIN يجب أن يكون 4 أرقام على الأقل', 'error');
|
765 |
+
return;
|
766 |
+
}
|
767 |
+
|
768 |
+
// محاكاة إنشاء الحساب
|
769 |
+
this.showAlert('تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول', 'success');
|
770 |
+
|
771 |
+
// ملء بيانات تسجيل الدخول
|
772 |
+
document.getElementById('phone-number').value = phone;
|
773 |
+
document.getElementById('pin-code').value = pin;
|
774 |
+
}
|
775 |
+
}
|
776 |
+
|
777 |
+
// تهيئة التطبيق عند تحميل الصفحة
|
778 |
+
document.addEventListener('DOMContentLoaded', () => {
|
779 |
+
window.app = new UnifiedWalletApp();
|
780 |
+
});
|
auth.js
ADDED
@@ -0,0 +1,426 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// نظام المصادقة والأمان
|
2 |
+
class AuthenticationManager {
|
3 |
+
constructor() {
|
4 |
+
this.currentUser = null;
|
5 |
+
this.sessionTimeout = 15 * 60 * 1000; // 15 دقيقة
|
6 |
+
this.maxLoginAttempts = 3;
|
7 |
+
this.lockoutDuration = 30 * 60 * 1000; // 30 دقيقة
|
8 |
+
this.biometricCredential = null;
|
9 |
+
|
10 |
+
this.init();
|
11 |
+
}
|
12 |
+
|
13 |
+
// تهيئة نظام المصادقة
|
14 |
+
init() {
|
15 |
+
this.setupSessionManagement();
|
16 |
+
this.loadStoredCredentials();
|
17 |
+
this.checkBiometricSupport();
|
18 |
+
}
|
19 |
+
|
20 |
+
// إعداد إدارة الجلسة
|
21 |
+
setupSessionManagement() {
|
22 |
+
// تحديث وقت النشاط عند التفاعل
|
23 |
+
document.addEventListener('click', () => this.updateLastActivity());
|
24 |
+
document.addEventListener('keypress', () => this.updateLastActivity());
|
25 |
+
document.addEventListener('touchstart', () => this.updateLastActivity());
|
26 |
+
|
27 |
+
// فحص انتهاء الجلسة كل دقيقة
|
28 |
+
setInterval(() => this.checkSessionExpiry(), 60000);
|
29 |
+
}
|
30 |
+
|
31 |
+
// تسجيل دخول بالرقم ورمز PIN
|
32 |
+
async loginWithPin(phoneNumber, pin) {
|
33 |
+
try {
|
34 |
+
// التحقق من محاولات تسجيل الدخول
|
35 |
+
if (this.isAccountLocked(phoneNumber)) {
|
36 |
+
const lockoutTime = this.getLockoutRemainingTime(phoneNumber);
|
37 |
+
throw new Error(`الحساب مقفل. المحاولة مرة أخرى خلال ${Math.ceil(lockoutTime / 60000)} دقيقة`);
|
38 |
+
}
|
39 |
+
|
40 |
+
// التحقق من صحة البيانات
|
41 |
+
if (!this.validatePhoneNumber(phoneNumber)) {
|
42 |
+
throw new Error('رقم الهاتف غير صحيح');
|
43 |
+
}
|
44 |
+
|
45 |
+
if (!this.validatePin(pin)) {
|
46 |
+
throw new Error('رمز PIN يجب أن يكون 4-6 أرقام');
|
47 |
+
}
|
48 |
+
|
49 |
+
// محاكاة التحقق من البيانات
|
50 |
+
const isValid = await this.verifyCredentials(phoneNumber, pin);
|
51 |
+
|
52 |
+
if (!isValid) {
|
53 |
+
this.recordFailedAttempt(phoneNumber);
|
54 |
+
const remainingAttempts = this.getRemainingAttempts(phoneNumber);
|
55 |
+
|
56 |
+
if (remainingAttempts <= 0) {
|
57 |
+
this.lockAccount(phoneNumber);
|
58 |
+
throw new Error('تم قفل الحساب بسبب المحاولات الخاطئة المتكررة');
|
59 |
+
}
|
60 |
+
|
61 |
+
throw new Error(`بيانات تسجيل الدخول غير صحيحة. المحاولات المتبقية: ${remainingAttempts}`);
|
62 |
+
}
|
63 |
+
|
64 |
+
// تسجيل دخول ناجح
|
65 |
+
this.clearFailedAttempts(phoneNumber);
|
66 |
+
const user = await this.createUserSession(phoneNumber, pin);
|
67 |
+
|
68 |
+
return user;
|
69 |
+
|
70 |
+
} catch (error) {
|
71 |
+
console.error('خطأ في تسجيل الدخول:', error);
|
72 |
+
throw error;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
// تسجيل دخول بالبصمة
|
77 |
+
async loginWithBiometric() {
|
78 |
+
try {
|
79 |
+
if (!this.isBiometricSupported()) {
|
80 |
+
throw new Error('المصادقة البيومترية غير مدعومة في هذا المتصفح');
|
81 |
+
}
|
82 |
+
|
83 |
+
if (!this.biometricCredential) {
|
84 |
+
throw new Error('لم يتم تسجيل بصمة مسبقاً. يرجى تسجيل الدخول برمز PIN أولاً');
|
85 |
+
}
|
86 |
+
|
87 |
+
// التحقق من البصمة
|
88 |
+
const credential = await navigator.credentials.get({
|
89 |
+
publicKey: {
|
90 |
+
challenge: this.generateChallenge(),
|
91 |
+
allowCredentials: [{
|
92 |
+
type: 'public-key',
|
93 |
+
id: this.biometricCredential.id
|
94 |
+
}],
|
95 |
+
timeout: 60000,
|
96 |
+
userVerification: 'required'
|
97 |
+
}
|
98 |
+
});
|
99 |
+
|
100 |
+
if (!credential) {
|
101 |
+
throw new Error('فشل في التحقق من البصمة');
|
102 |
+
}
|
103 |
+
|
104 |
+
// إنشاء جلسة المستخدم
|
105 |
+
const savedUser = this.getStoredUser();
|
106 |
+
if (!savedUser) {
|
107 |
+
throw new Error('لم يتم العثور على بيانات المستخدم');
|
108 |
+
}
|
109 |
+
|
110 |
+
const user = await this.createUserSession(savedUser.phone, null, 'biometric');
|
111 |
+
return user;
|
112 |
+
|
113 |
+
} catch (error) {
|
114 |
+
console.error('خطأ في تسجيل الدخول بالبصمة:', error);
|
115 |
+
throw error;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
// تسجيل البصمة
|
120 |
+
async registerBiometric(phoneNumber) {
|
121 |
+
try {
|
122 |
+
if (!this.isBiometricSupported()) {
|
123 |
+
throw new Error('المصادقة البيومترية غير مدعومة');
|
124 |
+
}
|
125 |
+
|
126 |
+
const credential = await navigator.credentials.create({
|
127 |
+
publicKey: {
|
128 |
+
challenge: this.generateChallenge(),
|
129 |
+
rp: {
|
130 |
+
name: 'محفظتي الموحدة',
|
131 |
+
id: window.location.hostname
|
132 |
+
},
|
133 |
+
user: {
|
134 |
+
id: new TextEncoder().encode(phoneNumber),
|
135 |
+
name: phoneNumber,
|
136 |
+
displayName: `مستخدم ${phoneNumber}`
|
137 |
+
},
|
138 |
+
pubKeyCredParams: [{
|
139 |
+
type: 'public-key',
|
140 |
+
alg: -7 // ES256
|
141 |
+
}],
|
142 |
+
timeout: 60000,
|
143 |
+
attestation: 'none',
|
144 |
+
authenticatorSelection: {
|
145 |
+
authenticatorAttachment: 'platform',
|
146 |
+
userVerification: 'required'
|
147 |
+
}
|
148 |
+
}
|
149 |
+
});
|
150 |
+
|
151 |
+
if (!credential) {
|
152 |
+
throw new Error('فشل في تسجيل البصمة');
|
153 |
+
}
|
154 |
+
|
155 |
+
// حفظ بيانات البصمة
|
156 |
+
this.biometricCredential = {
|
157 |
+
id: credential.rawId,
|
158 |
+
publicKey: credential.response.publicKey,
|
159 |
+
phoneNumber: phoneNumber,
|
160 |
+
registeredAt: new Date().toISOString()
|
161 |
+
};
|
162 |
+
|
163 |
+
this.storeBiometricCredential();
|
164 |
+
return true;
|
165 |
+
|
166 |
+
} catch (error) {
|
167 |
+
console.error('خطأ في تسجيل البصمة:', error);
|
168 |
+
throw error;
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
// إنشاء جلسة مستخدم
|
173 |
+
async createUserSession(phoneNumber, pin, authMethod = 'pin') {
|
174 |
+
const user = {
|
175 |
+
id: this.generateUserId(),
|
176 |
+
phone: phoneNumber,
|
177 |
+
name: this.extractNameFromPhone(phoneNumber),
|
178 |
+
authMethod: authMethod,
|
179 |
+
loginTime: new Date().toISOString(),
|
180 |
+
lastActivity: new Date().toISOString(),
|
181 |
+
sessionId: this.generateSessionId()
|
182 |
+
};
|
183 |
+
|
184 |
+
this.currentUser = user;
|
185 |
+
this.storeUserSession(user);
|
186 |
+
this.updateLastActivity();
|
187 |
+
|
188 |
+
return user;
|
189 |
+
}
|
190 |
+
|
191 |
+
// تسجيل الخروج
|
192 |
+
logout() {
|
193 |
+
this.currentUser = null;
|
194 |
+
this.clearUserSession();
|
195 |
+
this.clearStoredCredentials();
|
196 |
+
|
197 |
+
// إعادة توجيه لشاشة تسجيل الدخول
|
198 |
+
if (window.app) {
|
199 |
+
window.app.switchScreen('login');
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
// التحقق من صحة الجلسة
|
204 |
+
isSessionValid() {
|
205 |
+
if (!this.currentUser) {
|
206 |
+
return false;
|
207 |
+
}
|
208 |
+
|
209 |
+
const lastActivity = new Date(this.currentUser.lastActivity);
|
210 |
+
const now = new Date();
|
211 |
+
const timeDiff = now.getTime() - lastActivity.getTime();
|
212 |
+
|
213 |
+
return timeDiff < this.sessionTimeout;
|
214 |
+
}
|
215 |
+
|
216 |
+
// فحص انتهاء الجلسة
|
217 |
+
checkSessionExpiry() {
|
218 |
+
if (this.currentUser && !this.isSessionValid()) {
|
219 |
+
this.showSessionExpiredDialog();
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
// عرض حوار انتهاء الجلسة
|
224 |
+
showSessionExpiredDialog() {
|
225 |
+
if (confirm('انتهت صلاحية الجلسة. هل تريد تسجيل الدخول مرة أخرى؟')) {
|
226 |
+
this.logout();
|
227 |
+
} else {
|
228 |
+
this.logout();
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
// تحديث وقت النشاط الأخير
|
233 |
+
updateLastActivity() {
|
234 |
+
if (this.currentUser) {
|
235 |
+
this.currentUser.lastActivity = new Date().toISOString();
|
236 |
+
this.storeUserSession(this.currentUser);
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
// التحقق من قفل الحساب
|
241 |
+
isAccountLocked(phoneNumber) {
|
242 |
+
const lockData = this.getLockData(phoneNumber);
|
243 |
+
if (!lockData) return false;
|
244 |
+
|
245 |
+
const now = new Date().getTime();
|
246 |
+
return now < lockData.lockedUntil;
|
247 |
+
}
|
248 |
+
|
249 |
+
// الحصول على الوقت المتبقي للقفل
|
250 |
+
getLockoutRemainingTime(phoneNumber) {
|
251 |
+
const lockData = this.getLockData(phoneNumber);
|
252 |
+
if (!lockData) return 0;
|
253 |
+
|
254 |
+
const now = new Date().getTime();
|
255 |
+
return Math.max(0, lockData.lockedUntil - now);
|
256 |
+
}
|
257 |
+
|
258 |
+
// تسجيل محاولة فاشلة
|
259 |
+
recordFailedAttempt(phoneNumber) {
|
260 |
+
const attempts = this.getFailedAttempts(phoneNumber);
|
261 |
+
attempts.push(new Date().toISOString());
|
262 |
+
|
263 |
+
localStorage.setItem(`failed_attempts_${phoneNumber}`, JSON.stringify(attempts));
|
264 |
+
}
|
265 |
+
|
266 |
+
// الحصول على المحاولات الفاشلة
|
267 |
+
getFailedAttempts(phoneNumber) {
|
268 |
+
const stored = localStorage.getItem(`failed_attempts_${phoneNumber}`);
|
269 |
+
return stored ? JSON.parse(stored) : [];
|
270 |
+
}
|
271 |
+
|
272 |
+
// الحصول على المحاولات المتبقية
|
273 |
+
getRemainingAttempts(phoneNumber) {
|
274 |
+
const attempts = this.getFailedAttempts(phoneNumber);
|
275 |
+
return Math.max(0, this.maxLoginAttempts - attempts.length);
|
276 |
+
}
|
277 |
+
|
278 |
+
// قفل الحساب
|
279 |
+
lockAccount(phoneNumber) {
|
280 |
+
const lockData = {
|
281 |
+
lockedAt: new Date().toISOString(),
|
282 |
+
lockedUntil: new Date().getTime() + this.lockoutDuration
|
283 |
+
};
|
284 |
+
|
285 |
+
localStorage.setItem(`account_lock_${phoneNumber}`, JSON.stringify(lockData));
|
286 |
+
}
|
287 |
+
|
288 |
+
// الحصول على بيانات القفل
|
289 |
+
getLockData(phoneNumber) {
|
290 |
+
const stored = localStorage.getItem(`account_lock_${phoneNumber}`);
|
291 |
+
return stored ? JSON.parse(stored) : null;
|
292 |
+
}
|
293 |
+
|
294 |
+
// مسح المحاولات الفاشلة
|
295 |
+
clearFailedAttempts(phoneNumber) {
|
296 |
+
localStorage.removeItem(`failed_attempts_${phoneNumber}`);
|
297 |
+
localStorage.removeItem(`account_lock_${phoneNumber}`);
|
298 |
+
}
|
299 |
+
|
300 |
+
// التحقق من صحة رقم الهاتف
|
301 |
+
validatePhoneNumber(phoneNumber) {
|
302 |
+
return /^7[0-9]{8}$/.test(phoneNumber);
|
303 |
+
}
|
304 |
+
|
305 |
+
// التحقق من صحة رمز PIN
|
306 |
+
validatePin(pin) {
|
307 |
+
return /^[0-9]{4,6}$/.test(pin);
|
308 |
+
}
|
309 |
+
|
310 |
+
// محاكاة التحقق من البيانات
|
311 |
+
async verifyCredentials(phoneNumber, pin) {
|
312 |
+
// محاكاة استدعاء API
|
313 |
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
314 |
+
|
315 |
+
// في التطبيق الحقيقي، سيتم التحقق من البيانات مع الخادم
|
316 |
+
// للتجربة، نقبل أي رقم هاتف صحيح ورمز PIN من 4-6 أرقام
|
317 |
+
return this.validatePhoneNumber(phoneNumber) && this.validatePin(pin);
|
318 |
+
}
|
319 |
+
|
320 |
+
// فحص دعم المصادقة البيومترية
|
321 |
+
isBiometricSupported() {
|
322 |
+
return window.PublicKeyCredential &&
|
323 |
+
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable;
|
324 |
+
}
|
325 |
+
|
326 |
+
// فحص دعم المصادقة البيومترية
|
327 |
+
async checkBiometricSupport() {
|
328 |
+
if (this.isBiometricSupported()) {
|
329 |
+
try {
|
330 |
+
const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
331 |
+
return available;
|
332 |
+
} catch (error) {
|
333 |
+
console.warn('خطأ في فحص دعم المصادقة البيومترية:', error);
|
334 |
+
return false;
|
335 |
+
}
|
336 |
+
}
|
337 |
+
return false;
|
338 |
+
}
|
339 |
+
|
340 |
+
// توليد تحدي للمصادقة البيومترية
|
341 |
+
generateChallenge() {
|
342 |
+
const array = new Uint8Array(32);
|
343 |
+
crypto.getRandomValues(array);
|
344 |
+
return array;
|
345 |
+
}
|
346 |
+
|
347 |
+
// توليد معرف مستخدم
|
348 |
+
generateUserId() {
|
349 |
+
return 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
350 |
+
}
|
351 |
+
|
352 |
+
// توليد معرف جلسة
|
353 |
+
generateSessionId() {
|
354 |
+
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
355 |
+
}
|
356 |
+
|
357 |
+
// استخراج اسم من رقم الهاتف
|
358 |
+
extractNameFromPhone(phoneNumber) {
|
359 |
+
return `مستخدم ${phoneNumber.substr(-4)}`;
|
360 |
+
}
|
361 |
+
|
362 |
+
// حفظ جلسة المستخدم
|
363 |
+
storeUserSession(user) {
|
364 |
+
localStorage.setItem('unifiedWallet_session', JSON.stringify(user));
|
365 |
+
}
|
366 |
+
|
367 |
+
// تحميل جلسة المستخدم
|
368 |
+
loadStoredSession() {
|
369 |
+
const stored = localStorage.getItem('unifiedWallet_session');
|
370 |
+
if (stored) {
|
371 |
+
const user = JSON.parse(stored);
|
372 |
+
if (this.isSessionValid()) {
|
373 |
+
this.currentUser = user;
|
374 |
+
return user;
|
375 |
+
} else {
|
376 |
+
this.clearUserSession();
|
377 |
+
}
|
378 |
+
}
|
379 |
+
return null;
|
380 |
+
}
|
381 |
+
|
382 |
+
// مسح جلسة المستخدم
|
383 |
+
clearUserSession() {
|
384 |
+
localStorage.removeItem('unifiedWallet_session');
|
385 |
+
}
|
386 |
+
|
387 |
+
// حفظ بيانات البصمة
|
388 |
+
storeBiometricCredential() {
|
389 |
+
if (this.biometricCredential) {
|
390 |
+
localStorage.setItem('unifiedWallet_biometric', JSON.stringify(this.biometricCredential));
|
391 |
+
}
|
392 |
+
}
|
393 |
+
|
394 |
+
// تحميل بيانات البصمة
|
395 |
+
loadStoredCredentials() {
|
396 |
+
const stored = localStorage.getItem('unifiedWallet_biometric');
|
397 |
+
if (stored) {
|
398 |
+
this.biometricCredential = JSON.parse(stored);
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
// مسح بيانات البصمة
|
403 |
+
clearStoredCredentials() {
|
404 |
+
localStorage.removeItem('unifiedWallet_biometric');
|
405 |
+
this.biometricCredential = null;
|
406 |
+
}
|
407 |
+
|
408 |
+
// الحصول على المستخدم المحفوظ
|
409 |
+
getStoredUser() {
|
410 |
+
const stored = localStorage.getItem('unifiedWallet_user');
|
411 |
+
return stored ? JSON.parse(stored) : null;
|
412 |
+
}
|
413 |
+
|
414 |
+
// الحصول على المستخدم الحالي
|
415 |
+
getCurrentUser() {
|
416 |
+
return this.currentUser;
|
417 |
+
}
|
418 |
+
|
419 |
+
// التحقق من تسجيل الدخول
|
420 |
+
isLoggedIn() {
|
421 |
+
return this.currentUser !== null && this.isSessionValid();
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
// تصدير الكلاس للاستخدام في التطبيق الرئيسي
|
426 |
+
window.AuthenticationManager = AuthenticationManager;
|
capacitor-simple.config.ts
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { CapacitorConfig } from '@capacitor/cli';
|
2 |
+
|
3 |
+
const config: CapacitorConfig = {
|
4 |
+
appId: 'com.almada.unifiedwallet',
|
5 |
+
appName: 'محفظتي الموحدة',
|
6 |
+
webDir: '.',
|
7 |
+
bundledWebRuntime: false,
|
8 |
+
server: {
|
9 |
+
androidScheme: 'https'
|
10 |
+
},
|
11 |
+
plugins: {
|
12 |
+
SplashScreen: {
|
13 |
+
launchShowDuration: 3000,
|
14 |
+
launchAutoHide: true,
|
15 |
+
backgroundColor: "#4361ee",
|
16 |
+
androidSplashResourceName: "splash",
|
17 |
+
androidScaleType: "CENTER_CROP",
|
18 |
+
showSpinner: false,
|
19 |
+
splashFullScreen: true,
|
20 |
+
splashImmersive: true
|
21 |
+
},
|
22 |
+
StatusBar: {
|
23 |
+
style: "LIGHT",
|
24 |
+
backgroundColor: "#4361ee"
|
25 |
+
},
|
26 |
+
Keyboard: {
|
27 |
+
resize: "body",
|
28 |
+
style: "dark",
|
29 |
+
resizeOnFullScreen: true
|
30 |
+
},
|
31 |
+
LocalNotifications: {
|
32 |
+
smallIcon: "ic_stat_icon_config_sample",
|
33 |
+
iconColor: "#4361ee",
|
34 |
+
sound: "beep.wav"
|
35 |
+
},
|
36 |
+
Device: {
|
37 |
+
permissions: {
|
38 |
+
device: "تحتاج هذه الميزة لتحديد هوية الجهاز للأمان"
|
39 |
+
}
|
40 |
+
}
|
41 |
+
},
|
42 |
+
android: {
|
43 |
+
allowMixedContent: true,
|
44 |
+
captureInput: true,
|
45 |
+
webContentsDebuggingEnabled: true
|
46 |
+
}
|
47 |
+
};
|
48 |
+
|
49 |
+
export default config;
|
capacitor.config.js
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const config = {
|
2 |
+
appId: 'com.almada.unifiedwallet',
|
3 |
+
appName: 'محفظتي الموحدة',
|
4 |
+
webDir: 'www',
|
5 |
+
bundledWebRuntime: false,
|
6 |
+
server: {
|
7 |
+
androidScheme: 'https'
|
8 |
+
},
|
9 |
+
plugins: {
|
10 |
+
SplashScreen: {
|
11 |
+
launchShowDuration: 3000,
|
12 |
+
launchAutoHide: true,
|
13 |
+
backgroundColor: "#4361ee",
|
14 |
+
androidSplashResourceName: "splash",
|
15 |
+
androidScaleType: "CENTER_CROP",
|
16 |
+
showSpinner: false,
|
17 |
+
splashFullScreen: true,
|
18 |
+
splashImmersive: true
|
19 |
+
},
|
20 |
+
StatusBar: {
|
21 |
+
style: "LIGHT",
|
22 |
+
backgroundColor: "#4361ee"
|
23 |
+
},
|
24 |
+
Keyboard: {
|
25 |
+
resize: "body",
|
26 |
+
style: "dark",
|
27 |
+
resizeOnFullScreen: true
|
28 |
+
},
|
29 |
+
LocalNotifications: {
|
30 |
+
smallIcon: "ic_stat_icon_config_sample",
|
31 |
+
iconColor: "#4361ee",
|
32 |
+
sound: "beep.wav"
|
33 |
+
},
|
34 |
+
Device: {
|
35 |
+
permissions: {
|
36 |
+
device: "تحتاج هذه الميزة لتحديد هوية الجهاز للأمان"
|
37 |
+
}
|
38 |
+
}
|
39 |
+
},
|
40 |
+
android: {
|
41 |
+
allowMixedContent: true,
|
42 |
+
captureInput: true,
|
43 |
+
webContentsDebuggingEnabled: true
|
44 |
+
}
|
45 |
+
};
|
46 |
+
|
47 |
+
module.exports = config;
|
capacitor.config.ts
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { CapacitorConfig } from '@capacitor/cli';
|
2 |
+
|
3 |
+
const config: CapacitorConfig = {
|
4 |
+
appId: 'com.almada.unifiedwallet',
|
5 |
+
appName: 'محفظتي الموحدة',
|
6 |
+
webDir: 'www',
|
7 |
+
server: {
|
8 |
+
androidScheme: 'https'
|
9 |
+
},
|
10 |
+
plugins: {
|
11 |
+
SplashScreen: {
|
12 |
+
launchShowDuration: 3000,
|
13 |
+
launchAutoHide: true,
|
14 |
+
backgroundColor: "#4361ee",
|
15 |
+
androidSplashResourceName: "splash",
|
16 |
+
androidScaleType: "CENTER_CROP",
|
17 |
+
showSpinner: false,
|
18 |
+
androidSpinnerStyle: "large",
|
19 |
+
iosSpinnerStyle: "small",
|
20 |
+
spinnerColor: "#ffffff",
|
21 |
+
splashFullScreen: true,
|
22 |
+
splashImmersive: true,
|
23 |
+
layoutName: "launch_screen",
|
24 |
+
useDialog: true,
|
25 |
+
},
|
26 |
+
StatusBar: {
|
27 |
+
style: "LIGHT",
|
28 |
+
backgroundColor: "#4361ee"
|
29 |
+
},
|
30 |
+
Keyboard: {
|
31 |
+
resize: "body",
|
32 |
+
style: "dark",
|
33 |
+
resizeOnFullScreen: true
|
34 |
+
},
|
35 |
+
LocalNotifications: {
|
36 |
+
smallIcon: "ic_stat_icon_config_sample",
|
37 |
+
iconColor: "#4361ee",
|
38 |
+
sound: "beep.wav"
|
39 |
+
},
|
40 |
+
PushNotifications: {
|
41 |
+
presentationOptions: ["badge", "sound", "alert"]
|
42 |
+
},
|
43 |
+
Camera: {
|
44 |
+
permissions: {
|
45 |
+
camera: "تحتاج هذه الميزة للوصول إلى الكاميرا لمسح رموز QR",
|
46 |
+
photos: "تحتاج هذه الميزة للوصول إلى الصور لحفظ الإيصالات"
|
47 |
+
}
|
48 |
+
},
|
49 |
+
Device: {
|
50 |
+
permissions: {
|
51 |
+
device: "تحتاج هذه الميزة لتحديد هوية الجهاز للأمان"
|
52 |
+
}
|
53 |
+
}
|
54 |
+
},
|
55 |
+
android: {
|
56 |
+
allowMixedContent: true,
|
57 |
+
captureInput: true,
|
58 |
+
webContentsDebuggingEnabled: false
|
59 |
+
},
|
60 |
+
ios: {
|
61 |
+
contentInset: "automatic",
|
62 |
+
scrollEnabled: true,
|
63 |
+
allowsLinkPreview: false
|
64 |
+
}
|
65 |
+
};
|
66 |
+
|
67 |
+
export default config;
|
demo.html
ADDED
@@ -0,0 +1,333 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="ar" dir="rtl">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>تجربة محفظتي الموحدة</title>
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap" rel="stylesheet">
|
8 |
+
<style>
|
9 |
+
body {
|
10 |
+
font-family: 'Tajawal', sans-serif;
|
11 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
12 |
+
margin: 0;
|
13 |
+
padding: 20px;
|
14 |
+
min-height: 100vh;
|
15 |
+
display: flex;
|
16 |
+
align-items: center;
|
17 |
+
justify-content: center;
|
18 |
+
}
|
19 |
+
|
20 |
+
.demo-container {
|
21 |
+
background: white;
|
22 |
+
border-radius: 20px;
|
23 |
+
padding: 40px;
|
24 |
+
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
25 |
+
text-align: center;
|
26 |
+
max-width: 600px;
|
27 |
+
width: 100%;
|
28 |
+
}
|
29 |
+
|
30 |
+
.demo-header {
|
31 |
+
margin-bottom: 30px;
|
32 |
+
}
|
33 |
+
|
34 |
+
.demo-logo {
|
35 |
+
font-size: 4rem;
|
36 |
+
color: #4361ee;
|
37 |
+
margin-bottom: 20px;
|
38 |
+
}
|
39 |
+
|
40 |
+
.demo-title {
|
41 |
+
font-size: 2.5rem;
|
42 |
+
font-weight: 700;
|
43 |
+
color: #1f2937;
|
44 |
+
margin-bottom: 10px;
|
45 |
+
}
|
46 |
+
|
47 |
+
.demo-subtitle {
|
48 |
+
font-size: 1.2rem;
|
49 |
+
color: #6b7280;
|
50 |
+
margin-bottom: 30px;
|
51 |
+
}
|
52 |
+
|
53 |
+
.demo-features {
|
54 |
+
display: grid;
|
55 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
56 |
+
gap: 20px;
|
57 |
+
margin-bottom: 40px;
|
58 |
+
}
|
59 |
+
|
60 |
+
.feature-card {
|
61 |
+
background: #f8fafc;
|
62 |
+
border-radius: 15px;
|
63 |
+
padding: 25px;
|
64 |
+
border: 1px solid #e5e7eb;
|
65 |
+
}
|
66 |
+
|
67 |
+
.feature-icon {
|
68 |
+
font-size: 2.5rem;
|
69 |
+
color: #4361ee;
|
70 |
+
margin-bottom: 15px;
|
71 |
+
}
|
72 |
+
|
73 |
+
.feature-title {
|
74 |
+
font-size: 1.3rem;
|
75 |
+
font-weight: 600;
|
76 |
+
color: #1f2937;
|
77 |
+
margin-bottom: 10px;
|
78 |
+
}
|
79 |
+
|
80 |
+
.feature-description {
|
81 |
+
color: #6b7280;
|
82 |
+
line-height: 1.6;
|
83 |
+
}
|
84 |
+
|
85 |
+
.demo-wallets {
|
86 |
+
margin-bottom: 40px;
|
87 |
+
}
|
88 |
+
|
89 |
+
.wallets-grid {
|
90 |
+
display: grid;
|
91 |
+
grid-template-columns: repeat(3, 1fr);
|
92 |
+
gap: 15px;
|
93 |
+
margin-top: 20px;
|
94 |
+
}
|
95 |
+
|
96 |
+
.wallet-item {
|
97 |
+
background: white;
|
98 |
+
border: 2px solid #e5e7eb;
|
99 |
+
border-radius: 12px;
|
100 |
+
padding: 15px;
|
101 |
+
transition: all 0.3s ease;
|
102 |
+
}
|
103 |
+
|
104 |
+
.wallet-item:hover {
|
105 |
+
border-color: #4361ee;
|
106 |
+
transform: translateY(-5px);
|
107 |
+
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
108 |
+
}
|
109 |
+
|
110 |
+
.wallet-logo {
|
111 |
+
width: 50px;
|
112 |
+
height: 50px;
|
113 |
+
border-radius: 8px;
|
114 |
+
margin: 0 auto 10px;
|
115 |
+
overflow: hidden;
|
116 |
+
}
|
117 |
+
|
118 |
+
.wallet-logo img {
|
119 |
+
width: 100%;
|
120 |
+
height: 100%;
|
121 |
+
object-fit: cover;
|
122 |
+
}
|
123 |
+
|
124 |
+
.wallet-name {
|
125 |
+
font-weight: 600;
|
126 |
+
color: #1f2937;
|
127 |
+
font-size: 0.9rem;
|
128 |
+
}
|
129 |
+
|
130 |
+
.demo-actions {
|
131 |
+
display: flex;
|
132 |
+
gap: 20px;
|
133 |
+
justify-content: center;
|
134 |
+
flex-wrap: wrap;
|
135 |
+
}
|
136 |
+
|
137 |
+
.demo-btn {
|
138 |
+
background: #4361ee;
|
139 |
+
color: white;
|
140 |
+
border: none;
|
141 |
+
border-radius: 12px;
|
142 |
+
padding: 15px 30px;
|
143 |
+
font-size: 1.1rem;
|
144 |
+
font-weight: 600;
|
145 |
+
cursor: pointer;
|
146 |
+
transition: all 0.3s ease;
|
147 |
+
text-decoration: none;
|
148 |
+
display: inline-flex;
|
149 |
+
align-items: center;
|
150 |
+
gap: 10px;
|
151 |
+
}
|
152 |
+
|
153 |
+
.demo-btn:hover {
|
154 |
+
background: #3f37c9;
|
155 |
+
transform: translateY(-2px);
|
156 |
+
box-shadow: 0 10px 20px rgba(67, 97, 238, 0.3);
|
157 |
+
}
|
158 |
+
|
159 |
+
.demo-btn.secondary {
|
160 |
+
background: #6b7280;
|
161 |
+
}
|
162 |
+
|
163 |
+
.demo-btn.secondary:hover {
|
164 |
+
background: #4b5563;
|
165 |
+
}
|
166 |
+
|
167 |
+
.demo-info {
|
168 |
+
background: #f0f9ff;
|
169 |
+
border: 1px solid #0ea5e9;
|
170 |
+
border-radius: 12px;
|
171 |
+
padding: 20px;
|
172 |
+
margin-top: 30px;
|
173 |
+
}
|
174 |
+
|
175 |
+
.demo-info-title {
|
176 |
+
color: #0369a1;
|
177 |
+
font-weight: 600;
|
178 |
+
margin-bottom: 10px;
|
179 |
+
}
|
180 |
+
|
181 |
+
.demo-info-text {
|
182 |
+
color: #0c4a6e;
|
183 |
+
line-height: 1.6;
|
184 |
+
}
|
185 |
+
|
186 |
+
.credentials {
|
187 |
+
background: #fef3c7;
|
188 |
+
border: 1px solid #f59e0b;
|
189 |
+
border-radius: 12px;
|
190 |
+
padding: 20px;
|
191 |
+
margin-top: 20px;
|
192 |
+
}
|
193 |
+
|
194 |
+
.credentials-title {
|
195 |
+
color: #92400e;
|
196 |
+
font-weight: 600;
|
197 |
+
margin-bottom: 10px;
|
198 |
+
}
|
199 |
+
|
200 |
+
.credentials-list {
|
201 |
+
color: #78350f;
|
202 |
+
text-align: right;
|
203 |
+
}
|
204 |
+
|
205 |
+
@media (max-width: 768px) {
|
206 |
+
.demo-container {
|
207 |
+
padding: 20px;
|
208 |
+
margin: 10px;
|
209 |
+
}
|
210 |
+
|
211 |
+
.demo-title {
|
212 |
+
font-size: 2rem;
|
213 |
+
}
|
214 |
+
|
215 |
+
.wallets-grid {
|
216 |
+
grid-template-columns: repeat(2, 1fr);
|
217 |
+
}
|
218 |
+
|
219 |
+
.demo-actions {
|
220 |
+
flex-direction: column;
|
221 |
+
align-items: center;
|
222 |
+
}
|
223 |
+
|
224 |
+
.demo-btn {
|
225 |
+
width: 100%;
|
226 |
+
max-width: 300px;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
</style>
|
230 |
+
</head>
|
231 |
+
<body>
|
232 |
+
<div class="demo-container">
|
233 |
+
<div class="demo-header">
|
234 |
+
<div class="demo-logo">💳</div>
|
235 |
+
<h1 class="demo-title">محفظتي الموحدة</h1>
|
236 |
+
<p class="demo-subtitle">جميع محافظك الإلكترونية اليمنية في مكان واحد</p>
|
237 |
+
</div>
|
238 |
+
|
239 |
+
<div class="demo-features">
|
240 |
+
<div class="feature-card">
|
241 |
+
<div class="feature-icon">🔐</div>
|
242 |
+
<h3 class="feature-title">أمان متقدم</h3>
|
243 |
+
<p class="feature-description">نظام أمان متطور مع خيارات متعددة للتحقق (PIN، بصمة، OTP)</p>
|
244 |
+
</div>
|
245 |
+
|
246 |
+
<div class="feature-card">
|
247 |
+
<div class="feature-icon">💸</div>
|
248 |
+
<h3 class="feature-title">تحويلات سريعة</h3>
|
249 |
+
<p class="feature-description">تحويل الأموال بين المحافظ المختلفة بسهولة وأمان</p>
|
250 |
+
</div>
|
251 |
+
|
252 |
+
<div class="feature-card">
|
253 |
+
<div class="feature-icon">📱</div>
|
254 |
+
<h3 class="feature-title">واجهة موحدة</h3>
|
255 |
+
<p class="feature-description">إدارة جميع محافظك من تطبيق واحد بواجهة عصرية</p>
|
256 |
+
</div>
|
257 |
+
</div>
|
258 |
+
|
259 |
+
<div class="demo-wallets">
|
260 |
+
<h3>المحافظ المدعومة</h3>
|
261 |
+
<div class="wallets-grid">
|
262 |
+
<div class="wallet-item">
|
263 |
+
<div class="wallet-logo">
|
264 |
+
<img src="https://play-lh.googleusercontent.com/NuKV_Snecpx633RHzIWvFauG32WBAk-jt3lcZOajw2w7VD8Hdt8h5Lb0pYLopB_Qk4Y=w240-h480" alt="جوالي">
|
265 |
+
</div>
|
266 |
+
<div class="wallet-name">جوالي</div>
|
267 |
+
</div>
|
268 |
+
|
269 |
+
<div class="wallet-item">
|
270 |
+
<div class="wallet-logo">
|
271 |
+
<img src="https://play-lh.googleusercontent.com/OF2xMRUmNH47BIaOTbv7GgvmLRTQbT1xHkFF_Ocswx6Jq5gfX4VlfAl_275UmjH2pg=w240-h480" alt="ONE Cash">
|
272 |
+
</div>
|
273 |
+
<div class="wallet-name">ONE Cash</div>
|
274 |
+
</div>
|
275 |
+
|
276 |
+
<div class="wallet-item">
|
277 |
+
<div class="wallet-logo">
|
278 |
+
<img src="https://play-lh.googleusercontent.com/zkV8HeO6iF2xa77ObHdKfAXfrfjU5fgWB0XsWt7_DmG4VGSKob2jU_CrqWyKQtghQyE=w240-h480" alt="Cash">
|
279 |
+
</div>
|
280 |
+
<div class="wallet-name">Cash</div>
|
281 |
+
</div>
|
282 |
+
|
283 |
+
<div class="wallet-item">
|
284 |
+
<div class="wallet-logo">
|
285 |
+
<img src="https://play-lh.googleusercontent.com/EAaXnjh1FVPYpI5qWZwWvZIV5oD1hm9auDX0owOgREBjMAzYKX9od1USWRzlXIhRvKMx=w240-h480" alt="Jaib">
|
286 |
+
</div>
|
287 |
+
<div class="wallet-name">Jaib</div>
|
288 |
+
</div>
|
289 |
+
|
290 |
+
<div class="wallet-item">
|
291 |
+
<div class="wallet-logo">
|
292 |
+
<img src="https://play-lh.googleusercontent.com/mNvakjEk-3VX7icU5w4xmAhT4MQgGAGcQYpRvPkBLVTzD-sYnmzAH_wuglMujTsaqQ=w240-h480" alt="mFloos">
|
293 |
+
</div>
|
294 |
+
<div class="wallet-name">mFloos</div>
|
295 |
+
</div>
|
296 |
+
|
297 |
+
<div class="wallet-item">
|
298 |
+
<div class="wallet-logo">
|
299 |
+
<img src="https://play-lh.googleusercontent.com/51r7PLMlK0gVvITgOoJ7BnGX-9Gq3_ayiSiHHxSDbJZPCgABXI_LnU6jNCHAefWHvSPV=w240-h480" alt="Mobile Money">
|
300 |
+
</div>
|
301 |
+
<div class="wallet-name">Mobile Money</div>
|
302 |
+
</div>
|
303 |
+
</div>
|
304 |
+
</div>
|
305 |
+
|
306 |
+
<div class="demo-actions">
|
307 |
+
<a href="index.html" class="demo-btn">
|
308 |
+
🚀 تجربة التطبيق
|
309 |
+
</a>
|
310 |
+
<a href="الاخير.html" class="demo-btn secondary">
|
311 |
+
📄 النظام الأصلي
|
312 |
+
</a>
|
313 |
+
</div>
|
314 |
+
|
315 |
+
<div class="credentials">
|
316 |
+
<div class="credentials-title">بيانات التجربة:</div>
|
317 |
+
<div class="credentials-list">
|
318 |
+
<div>رقم الهاتف: أي رقم يمني صحيح (9 أرقام)</div>
|
319 |
+
<div>رمز PIN: أي رمز من 4-6 أرقام</div>
|
320 |
+
<div>مثال: 777123456 / 1234</div>
|
321 |
+
</div>
|
322 |
+
</div>
|
323 |
+
|
324 |
+
<div class="demo-info">
|
325 |
+
<div class="demo-info-title">ملاحظة مهمة:</div>
|
326 |
+
<div class="demo-info-text">
|
327 |
+
هذا تطبيق تجريبي لأغراض العرض فقط. جميع البيانات والمعاملات محاكاة ولا تؤثر على الحسابات الحقيقية.
|
328 |
+
التطبيق يدعم المصادقة البيومترية في المتصفحات المتوافقة.
|
329 |
+
</div>
|
330 |
+
</div>
|
331 |
+
</div>
|
332 |
+
</body>
|
333 |
+
</html>
|
index.html
ADDED
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="ar" dir="rtl">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>محفظتي الموحدة - جميع المحافظ الإلكترونية اليمنية</title>
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap" rel="stylesheet">
|
8 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
9 |
+
<link rel="stylesheet" href="styles.css">
|
10 |
+
</head>
|
11 |
+
<body>
|
12 |
+
<!-- شاشة التحميل -->
|
13 |
+
<div id="loading-screen" class="loading-screen">
|
14 |
+
<div class="loading-content">
|
15 |
+
<div class="loading-logo">
|
16 |
+
<i class="fas fa-wallet"></i>
|
17 |
+
</div>
|
18 |
+
<h2>محفظتي الموحدة</h2>
|
19 |
+
<div class="loading-spinner"></div>
|
20 |
+
<p>جاري تحميل محافظك...</p>
|
21 |
+
</div>
|
22 |
+
</div>
|
23 |
+
|
24 |
+
<!-- شاشة تسجيل الدخول -->
|
25 |
+
<div id="login-screen" class="screen active">
|
26 |
+
<div class="login-container">
|
27 |
+
<div class="login-header">
|
28 |
+
<div class="app-logo">
|
29 |
+
<i class="fas fa-wallet"></i>
|
30 |
+
</div>
|
31 |
+
<h1>محفظتي الموحدة</h1>
|
32 |
+
<p>جميع محافظك الإلكترونية في مكان واحد</p>
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<div class="login-form">
|
36 |
+
<div class="form-group">
|
37 |
+
<label for="phone-number">رقم الهاتف</label>
|
38 |
+
<div class="input-group">
|
39 |
+
<span class="input-prefix">+967</span>
|
40 |
+
<input type="tel" id="phone-number" placeholder="7xxxxxxxx" maxlength="9">
|
41 |
+
</div>
|
42 |
+
</div>
|
43 |
+
|
44 |
+
<div class="form-group">
|
45 |
+
<label for="pin-code">رمز PIN</label>
|
46 |
+
<input type="password" id="pin-code" placeholder="أدخل رمز PIN" maxlength="6">
|
47 |
+
</div>
|
48 |
+
|
49 |
+
<button id="login-btn" class="btn-primary">
|
50 |
+
<i class="fas fa-sign-in-alt"></i>
|
51 |
+
تسجيل الدخول
|
52 |
+
</button>
|
53 |
+
|
54 |
+
<div class="alternative-login">
|
55 |
+
<p>أو</p>
|
56 |
+
<button id="biometric-login" class="btn-secondary">
|
57 |
+
<i class="fas fa-fingerprint"></i>
|
58 |
+
تسجيل الدخول بالبصمة
|
59 |
+
</button>
|
60 |
+
</div>
|
61 |
+
|
62 |
+
<div class="register-link">
|
63 |
+
<p>ليس لديك حساب؟ <a href="#" id="register-link">إنشاء حساب جديد</a></p>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
|
67 |
+
<div class="supported-wallets">
|
68 |
+
<h3>المحافظ المدعومة</h3>
|
69 |
+
<div class="wallet-logos">
|
70 |
+
<div class="wallet-logo" title="جوالي">
|
71 |
+
<img src="https://play-lh.googleusercontent.com/NuKV_Snecpx633RHzIWvFauG32WBAk-jt3lcZOajw2w7VD8Hdt8h5Lb0pYLopB_Qk4Y=w240-h480" alt="جوالي">
|
72 |
+
</div>
|
73 |
+
<div class="wallet-logo" title="ONE Cash">
|
74 |
+
<img src="https://play-lh.googleusercontent.com/OF2xMRUmNH47BIaOTbv7GgvmLRTQbT1xHkFF_Ocswx6Jq5gfX4VlfAl_275UmjH2pg=w240-h480" alt="ONE Cash">
|
75 |
+
</div>
|
76 |
+
<div class="wallet-logo" title="Cash">
|
77 |
+
<img src="https://play-lh.googleusercontent.com/zkV8HeO6iF2xa77ObHdKfAXfrfjU5fgWB0XsWt7_DmG4VGSKob2jU_CrqWyKQtghQyE=w240-h480" alt="Cash">
|
78 |
+
</div>
|
79 |
+
<div class="wallet-logo" title="Jaib">
|
80 |
+
<img src="https://play-lh.googleusercontent.com/EAaXnjh1FVPYpI5qWZwWvZIV5oD1hm9auDX0owOgREBjMAzYKX9od1USWRzlXIhRvKMx=w240-h480" alt="Jaib">
|
81 |
+
</div>
|
82 |
+
<div class="wallet-logo" title="mFloos">
|
83 |
+
<img src="https://play-lh.googleusercontent.com/mNvakjEk-3VX7icU5w4xmAhT4MQgGAGcQYpRvPkBLVTzD-sYnmzAH_wuglMujTsaqQ=w240-h480" alt="mFloos">
|
84 |
+
</div>
|
85 |
+
<div class="wallet-logo" title="Mobile Money">
|
86 |
+
<img src="https://play-lh.googleusercontent.com/51r7PLMlK0gVvITgOoJ7BnGX-9Gq3_ayiSiHHxSDbJZPCgABXI_LnU6jNCHAefWHvSPV=w240-h480" alt="Mobile Money">
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
</div>
|
90 |
+
</div>
|
91 |
+
</div>
|
92 |
+
|
93 |
+
<!-- الشاشة الرئيسية -->
|
94 |
+
<div id="main-screen" class="screen">
|
95 |
+
<header class="main-header">
|
96 |
+
<div class="header-top">
|
97 |
+
<div class="user-info">
|
98 |
+
<div class="user-avatar">
|
99 |
+
<i class="fas fa-user"></i>
|
100 |
+
</div>
|
101 |
+
<div class="user-details">
|
102 |
+
<h3 id="user-name">مرحباً بك</h3>
|
103 |
+
<p id="user-phone">+967 7xxxxxxxx</p>
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
<div class="header-actions">
|
107 |
+
<button class="icon-btn" id="notifications-btn">
|
108 |
+
<i class="fas fa-bell"></i>
|
109 |
+
<span class="badge">3</span>
|
110 |
+
</button>
|
111 |
+
<button class="icon-btn" id="settings-btn">
|
112 |
+
<i class="fas fa-cog"></i>
|
113 |
+
</button>
|
114 |
+
</div>
|
115 |
+
</div>
|
116 |
+
|
117 |
+
<div class="total-balance">
|
118 |
+
<h2>إجمالي الرصيد</h2>
|
119 |
+
<div class="balance-amount" id="total-balance">0 ر.ي</div>
|
120 |
+
<div class="balance-actions">
|
121 |
+
<button class="btn-small" id="refresh-balance">
|
122 |
+
<i class="fas fa-sync-alt"></i>
|
123 |
+
تحديث
|
124 |
+
</button>
|
125 |
+
<button class="btn-small" id="hide-balance">
|
126 |
+
<i class="fas fa-eye-slash"></i>
|
127 |
+
إخفاء
|
128 |
+
</button>
|
129 |
+
</div>
|
130 |
+
</div>
|
131 |
+
</header>
|
132 |
+
|
133 |
+
<main class="main-content">
|
134 |
+
<!-- قائمة الإجراءات السريعة -->
|
135 |
+
<section class="quick-actions">
|
136 |
+
<h3>الإجراءات السريعة</h3>
|
137 |
+
<div class="actions-grid">
|
138 |
+
<button class="action-btn" id="transfer-btn">
|
139 |
+
<i class="fas fa-exchange-alt"></i>
|
140 |
+
<span>تحويل</span>
|
141 |
+
</button>
|
142 |
+
<button class="action-btn" id="pay-bills-btn">
|
143 |
+
<i class="fas fa-file-invoice-dollar"></i>
|
144 |
+
<span>دفع فواتير</span>
|
145 |
+
</button>
|
146 |
+
<button class="action-btn" id="recharge-btn">
|
147 |
+
<i class="fas fa-mobile-alt"></i>
|
148 |
+
<span>شحن رصيد</span>
|
149 |
+
</button>
|
150 |
+
<button class="action-btn" id="qr-scan-btn">
|
151 |
+
<i class="fas fa-qrcode"></i>
|
152 |
+
<span>مسح QR</span>
|
153 |
+
</button>
|
154 |
+
</div>
|
155 |
+
</section>
|
156 |
+
|
157 |
+
<!-- قائمة المحافظ -->
|
158 |
+
<section class="wallets-section">
|
159 |
+
<div class="section-header">
|
160 |
+
<h3>محافظي</h3>
|
161 |
+
<button class="btn-link" id="manage-wallets">إدارة المحافظ</button>
|
162 |
+
</div>
|
163 |
+
<div class="wallets-list" id="wallets-list">
|
164 |
+
<!-- سيتم ملء المحافظ ديناميكياً -->
|
165 |
+
</div>
|
166 |
+
</section>
|
167 |
+
|
168 |
+
<!-- آخر المعاملات -->
|
169 |
+
<section class="transactions-section">
|
170 |
+
<div class="section-header">
|
171 |
+
<h3>آخر المعاملات</h3>
|
172 |
+
<button class="btn-link" id="view-all-transactions">عرض الكل</button>
|
173 |
+
</div>
|
174 |
+
<div class="transactions-list" id="transactions-list">
|
175 |
+
<!-- سيتم ملء المعاملات ديناميكياً -->
|
176 |
+
</div>
|
177 |
+
</section>
|
178 |
+
</main>
|
179 |
+
|
180 |
+
<!-- شريط التنقل السفلي -->
|
181 |
+
<nav class="bottom-nav">
|
182 |
+
<button class="nav-item active" data-screen="main">
|
183 |
+
<i class="fas fa-home"></i>
|
184 |
+
<span>الرئيسية</span>
|
185 |
+
</button>
|
186 |
+
<button class="nav-item" data-screen="transfer">
|
187 |
+
<i class="fas fa-exchange-alt"></i>
|
188 |
+
<span>تحويل</span>
|
189 |
+
</button>
|
190 |
+
<button class="nav-item" data-screen="history">
|
191 |
+
<i class="fas fa-history"></i>
|
192 |
+
<span>السجل</span>
|
193 |
+
</button>
|
194 |
+
<button class="nav-item" data-screen="profile">
|
195 |
+
<i class="fas fa-user"></i>
|
196 |
+
<span>الملف الشخصي</span>
|
197 |
+
</button>
|
198 |
+
</nav>
|
199 |
+
</div>
|
200 |
+
|
201 |
+
<!-- شاشة التحويل -->
|
202 |
+
<div id="transfer-screen" class="screen">
|
203 |
+
<header class="screen-header">
|
204 |
+
<button class="back-btn" id="transfer-back">
|
205 |
+
<i class="fas fa-arrow-right"></i>
|
206 |
+
</button>
|
207 |
+
<h2>تحويل الأموال</h2>
|
208 |
+
</header>
|
209 |
+
|
210 |
+
<div class="transfer-content">
|
211 |
+
<div class="transfer-steps">
|
212 |
+
<div class="step-indicator">
|
213 |
+
<div class="step active" id="transfer-step-1">1</div>
|
214 |
+
<div class="step-line"></div>
|
215 |
+
<div class="step" id="transfer-step-2">2</div>
|
216 |
+
<div class="step-line"></div>
|
217 |
+
<div class="step" id="transfer-step-3">3</div>
|
218 |
+
</div>
|
219 |
+
<div class="step-labels">
|
220 |
+
<span>اختيار المحفظة</span>
|
221 |
+
<span>تفاصيل التحويل</span>
|
222 |
+
<span>التأكيد</span>
|
223 |
+
</div>
|
224 |
+
</div>
|
225 |
+
|
226 |
+
<!-- الخطوة الأولى: اختيار المحفظة -->
|
227 |
+
<div class="transfer-step-content active" id="transfer-step-content-1">
|
228 |
+
<h3>اختر المحفظة المرسلة</h3>
|
229 |
+
<div class="wallet-selection" id="source-wallet-selection">
|
230 |
+
<!-- سيتم ملء المحافظ ديناميكياً -->
|
231 |
+
</div>
|
232 |
+
</div>
|
233 |
+
|
234 |
+
<!-- الخطوة الثانية: تفاصيل التحويل -->
|
235 |
+
<div class="transfer-step-content" id="transfer-step-content-2">
|
236 |
+
<h3>تفاصيل التحويل</h3>
|
237 |
+
|
238 |
+
<div class="form-group">
|
239 |
+
<label>المحفظة المستقبلة</label>
|
240 |
+
<select id="destination-wallet" class="form-control">
|
241 |
+
<option value="">-- اختر المحفظة --</option>
|
242 |
+
<option value="jawali">جوالي</option>
|
243 |
+
<option value="onecash">ONE Cash</option>
|
244 |
+
<option value="cash">Cash</option>
|
245 |
+
<option value="jaib">Jaib</option>
|
246 |
+
<option value="mfloos">mFloos</option>
|
247 |
+
<option value="mobilemoney">Mobile Money</option>
|
248 |
+
</select>
|
249 |
+
</div>
|
250 |
+
|
251 |
+
<div class="form-group">
|
252 |
+
<label>رقم المستقبل</label>
|
253 |
+
<div class="input-group">
|
254 |
+
<span class="input-prefix">+967</span>
|
255 |
+
<input type="tel" id="recipient-number" placeholder="7xxxxxxxx" maxlength="9" class="form-control">
|
256 |
+
</div>
|
257 |
+
</div>
|
258 |
+
|
259 |
+
<div class="form-group">
|
260 |
+
<label>المبلغ (ر.ي)</label>
|
261 |
+
<input type="number" id="transfer-amount" placeholder="أدخل المبلغ" class="form-control">
|
262 |
+
<div class="amount-suggestions">
|
263 |
+
<button type="button" class="amount-btn" data-amount="100">100</button>
|
264 |
+
<button type="button" class="amount-btn" data-amount="500">500</button>
|
265 |
+
<button type="button" class="amount-btn" data-amount="1000">1000</button>
|
266 |
+
<button type="button" class="amount-btn" data-amount="5000">5000</button>
|
267 |
+
</div>
|
268 |
+
</div>
|
269 |
+
|
270 |
+
<div class="form-group">
|
271 |
+
<label>ملاحظة (اختياري)</label>
|
272 |
+
<textarea id="transfer-note" placeholder="أضف ملاحظة..." rows="3" class="form-control"></textarea>
|
273 |
+
</div>
|
274 |
+
</div>
|
275 |
+
|
276 |
+
<!-- الخطوة الثالثة: التأكيد -->
|
277 |
+
<div class="transfer-step-content" id="transfer-step-content-3">
|
278 |
+
<h3>تأكيد التحويل</h3>
|
279 |
+
|
280 |
+
<div class="transfer-summary">
|
281 |
+
<div class="summary-item">
|
282 |
+
<span>من:</span>
|
283 |
+
<strong id="summary-from-wallet">-</strong>
|
284 |
+
</div>
|
285 |
+
<div class="summary-item">
|
286 |
+
<span>إلى:</span>
|
287 |
+
<strong id="summary-to-wallet">-</strong>
|
288 |
+
</div>
|
289 |
+
<div class="summary-item">
|
290 |
+
<span>رقم المستقبل:</span>
|
291 |
+
<strong id="summary-recipient">-</strong>
|
292 |
+
</div>
|
293 |
+
<div class="summary-item">
|
294 |
+
<span>المبلغ:</span>
|
295 |
+
<strong id="summary-amount">-</strong>
|
296 |
+
</div>
|
297 |
+
<div class="summary-item">
|
298 |
+
<span>رسوم التحويل:</span>
|
299 |
+
<strong id="summary-fee">-</strong>
|
300 |
+
</div>
|
301 |
+
<div class="summary-item total">
|
302 |
+
<span>إجمالي المبلغ المخصوم:</span>
|
303 |
+
<strong id="summary-total">-</strong>
|
304 |
+
</div>
|
305 |
+
</div>
|
306 |
+
|
307 |
+
<div class="form-group">
|
308 |
+
<label>رمز PIN للتأكيد</label>
|
309 |
+
<input type="password" id="transfer-pin" placeholder="أدخل رمز PIN" maxlength="6" class="form-control">
|
310 |
+
</div>
|
311 |
+
</div>
|
312 |
+
|
313 |
+
<!-- أزرار التنقل -->
|
314 |
+
<div class="transfer-navigation">
|
315 |
+
<button type="button" id="transfer-prev-btn" class="btn-secondary" style="display: none;">
|
316 |
+
<i class="fas fa-arrow-right"></i>
|
317 |
+
السابق
|
318 |
+
</button>
|
319 |
+
<button type="button" id="transfer-next-btn" class="btn-primary">
|
320 |
+
التالي
|
321 |
+
<i class="fas fa-arrow-left"></i>
|
322 |
+
</button>
|
323 |
+
<button type="button" id="transfer-confirm-btn" class="btn-primary" style="display: none;">
|
324 |
+
<i class="fas fa-check"></i>
|
325 |
+
تأكيد التحويل
|
326 |
+
</button>
|
327 |
+
</div>
|
328 |
+
</div>
|
329 |
+
</div>
|
330 |
+
|
331 |
+
<!-- النوافذ المنبثقة والمودالز -->
|
332 |
+
<div id="modal-overlay" class="modal-overlay"></div>
|
333 |
+
|
334 |
+
<!-- Capacitor Core -->
|
335 |
+
<script type="module" src="https://unpkg.com/@capacitor/core@4/dist/capacitor.js"></script>
|
336 |
+
|
337 |
+
<!-- تحميل الملفات الخارجية -->
|
338 |
+
<script src="notifications.js"></script>
|
339 |
+
<script src="auth.js"></script>
|
340 |
+
<script src="wallets.js"></script>
|
341 |
+
<script src="app.js"></script>
|
342 |
+
|
343 |
+
<!-- Capacitor Initialization -->
|
344 |
+
<script>
|
345 |
+
// تهيئة Capacitor
|
346 |
+
if (window.Capacitor) {
|
347 |
+
console.log('Capacitor is available');
|
348 |
+
|
349 |
+
// إعداد الإشعارات
|
350 |
+
if (window.Capacitor.Plugins.LocalNotifications) {
|
351 |
+
window.Capacitor.Plugins.LocalNotifications.requestPermissions();
|
352 |
+
}
|
353 |
+
|
354 |
+
// إعداد شريط الحالة
|
355 |
+
if (window.Capacitor.Plugins.StatusBar) {
|
356 |
+
window.Capacitor.Plugins.StatusBar.setStyle({ style: 'LIGHT' });
|
357 |
+
window.Capacitor.Plugins.StatusBar.setBackgroundColor({ color: '#4361ee' });
|
358 |
+
}
|
359 |
+
|
360 |
+
// إخفاء شاشة البداية
|
361 |
+
if (window.Capacitor.Plugins.SplashScreen) {
|
362 |
+
setTimeout(() => {
|
363 |
+
window.Capacitor.Plugins.SplashScreen.hide();
|
364 |
+
}, 2000);
|
365 |
+
}
|
366 |
+
}
|
367 |
+
</script>
|
368 |
+
</body>
|
369 |
+
</html>
|
ionic.config.json
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "unified-wallet-app",
|
3 |
+
"integrations": {
|
4 |
+
"capacitor": {}
|
5 |
+
},
|
6 |
+
"type": "angular",
|
7 |
+
"id": "unified-wallet"
|
8 |
+
}
|
notifications.js
ADDED
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// نظام الإشعارات والتنبيهات
|
2 |
+
class NotificationManager {
|
3 |
+
constructor() {
|
4 |
+
this.notifications = [];
|
5 |
+
this.toastContainer = null;
|
6 |
+
this.modalContainer = null;
|
7 |
+
this.init();
|
8 |
+
}
|
9 |
+
|
10 |
+
// تهيئة نظام الإشعارات
|
11 |
+
init() {
|
12 |
+
this.createToastContainer();
|
13 |
+
this.createModalContainer();
|
14 |
+
this.loadStoredNotifications();
|
15 |
+
}
|
16 |
+
|
17 |
+
// إنشاء حاوية التوست
|
18 |
+
createToastContainer() {
|
19 |
+
this.toastContainer = document.createElement('div');
|
20 |
+
this.toastContainer.className = 'toast-container';
|
21 |
+
this.toastContainer.innerHTML = '';
|
22 |
+
document.body.appendChild(this.toastContainer);
|
23 |
+
}
|
24 |
+
|
25 |
+
// إنشاء حاوية المودال
|
26 |
+
createModalContainer() {
|
27 |
+
this.modalContainer = document.createElement('div');
|
28 |
+
this.modalContainer.className = 'modal-container';
|
29 |
+
this.modalContainer.innerHTML = `
|
30 |
+
<div class="modal-overlay" onclick="this.parentElement.style.display='none'"></div>
|
31 |
+
<div class="modal-content">
|
32 |
+
<div class="modal-header">
|
33 |
+
<h3 class="modal-title"></h3>
|
34 |
+
<button class="modal-close" onclick="this.closest('.modal-container').style.display='none'">
|
35 |
+
<i class="fas fa-times"></i>
|
36 |
+
</button>
|
37 |
+
</div>
|
38 |
+
<div class="modal-body"></div>
|
39 |
+
<div class="modal-footer"></div>
|
40 |
+
</div>
|
41 |
+
`;
|
42 |
+
document.body.appendChild(this.modalContainer);
|
43 |
+
}
|
44 |
+
|
45 |
+
// عرض تنبيه توست
|
46 |
+
showToast(message, type = 'info', duration = 5000) {
|
47 |
+
const toast = document.createElement('div');
|
48 |
+
toast.className = `toast toast-${type}`;
|
49 |
+
|
50 |
+
const icon = this.getToastIcon(type);
|
51 |
+
|
52 |
+
toast.innerHTML = `
|
53 |
+
<div class="toast-icon">
|
54 |
+
<i class="${icon}"></i>
|
55 |
+
</div>
|
56 |
+
<div class="toast-content">
|
57 |
+
<div class="toast-message">${message}</div>
|
58 |
+
</div>
|
59 |
+
<button class="toast-close" onclick="this.parentElement.remove()">
|
60 |
+
<i class="fas fa-times"></i>
|
61 |
+
</button>
|
62 |
+
`;
|
63 |
+
|
64 |
+
// إضافة التوست
|
65 |
+
this.toastContainer.appendChild(toast);
|
66 |
+
|
67 |
+
// تحريك التوست للداخل
|
68 |
+
setTimeout(() => {
|
69 |
+
toast.classList.add('toast-show');
|
70 |
+
}, 100);
|
71 |
+
|
72 |
+
// إزالة التوست تلقائياً
|
73 |
+
if (duration > 0) {
|
74 |
+
setTimeout(() => {
|
75 |
+
this.removeToast(toast);
|
76 |
+
}, duration);
|
77 |
+
}
|
78 |
+
|
79 |
+
return toast;
|
80 |
+
}
|
81 |
+
|
82 |
+
// إزالة التوست
|
83 |
+
removeToast(toast) {
|
84 |
+
toast.classList.add('toast-hide');
|
85 |
+
setTimeout(() => {
|
86 |
+
if (toast.parentElement) {
|
87 |
+
toast.parentElement.removeChild(toast);
|
88 |
+
}
|
89 |
+
}, 300);
|
90 |
+
}
|
91 |
+
|
92 |
+
// الحصول على أيقونة التوست
|
93 |
+
getToastIcon(type) {
|
94 |
+
const icons = {
|
95 |
+
success: 'fas fa-check-circle',
|
96 |
+
error: 'fas fa-exclamation-circle',
|
97 |
+
warning: 'fas fa-exclamation-triangle',
|
98 |
+
info: 'fas fa-info-circle',
|
99 |
+
loading: 'fas fa-spinner fa-spin'
|
100 |
+
};
|
101 |
+
return icons[type] || icons.info;
|
102 |
+
}
|
103 |
+
|
104 |
+
// عرض مودال
|
105 |
+
showModal(title, content, buttons = []) {
|
106 |
+
const modal = this.modalContainer;
|
107 |
+
|
108 |
+
modal.querySelector('.modal-title').textContent = title;
|
109 |
+
modal.querySelector('.modal-body').innerHTML = content;
|
110 |
+
|
111 |
+
// إضافة الأزرار
|
112 |
+
const footer = modal.querySelector('.modal-footer');
|
113 |
+
footer.innerHTML = '';
|
114 |
+
|
115 |
+
buttons.forEach(button => {
|
116 |
+
const btn = document.createElement('button');
|
117 |
+
btn.className = `btn ${button.class || 'btn-primary'}`;
|
118 |
+
btn.textContent = button.text;
|
119 |
+
btn.onclick = () => {
|
120 |
+
if (button.action) button.action();
|
121 |
+
modal.style.display = 'none';
|
122 |
+
};
|
123 |
+
footer.appendChild(btn);
|
124 |
+
});
|
125 |
+
|
126 |
+
// إضافة زر إغلاق افتراضي إذا لم توجد أزرار
|
127 |
+
if (buttons.length === 0) {
|
128 |
+
const closeBtn = document.createElement('button');
|
129 |
+
closeBtn.className = 'btn btn-secondary';
|
130 |
+
closeBtn.textContent = 'إغلاق';
|
131 |
+
closeBtn.onclick = () => modal.style.display = 'none';
|
132 |
+
footer.appendChild(closeBtn);
|
133 |
+
}
|
134 |
+
|
135 |
+
modal.style.display = 'flex';
|
136 |
+
return modal;
|
137 |
+
}
|
138 |
+
|
139 |
+
// عرض مودال تأكيد
|
140 |
+
showConfirm(title, message, onConfirm, onCancel) {
|
141 |
+
return this.showModal(title, `<p>${message}</p>`, [
|
142 |
+
{
|
143 |
+
text: 'تأكيد',
|
144 |
+
class: 'btn-primary',
|
145 |
+
action: onConfirm
|
146 |
+
},
|
147 |
+
{
|
148 |
+
text: 'إلغاء',
|
149 |
+
class: 'btn-secondary',
|
150 |
+
action: onCancel
|
151 |
+
}
|
152 |
+
]);
|
153 |
+
}
|
154 |
+
|
155 |
+
// عرض مودال تحميل
|
156 |
+
showLoading(message = 'جاري التحميل...') {
|
157 |
+
const loadingContent = `
|
158 |
+
<div class="loading-content">
|
159 |
+
<div class="loading-spinner"></div>
|
160 |
+
<p>${message}</p>
|
161 |
+
</div>
|
162 |
+
`;
|
163 |
+
|
164 |
+
const modal = this.showModal('', loadingContent, []);
|
165 |
+
modal.classList.add('loading-modal');
|
166 |
+
|
167 |
+
// منع إغلاق مودال التحميل بالنقر خارجه
|
168 |
+
modal.querySelector('.modal-overlay').onclick = null;
|
169 |
+
modal.querySelector('.modal-close').style.display = 'none';
|
170 |
+
|
171 |
+
return modal;
|
172 |
+
}
|
173 |
+
|
174 |
+
// إخفاء مودال التحميل
|
175 |
+
hideLoading() {
|
176 |
+
const loadingModal = document.querySelector('.loading-modal');
|
177 |
+
if (loadingModal) {
|
178 |
+
loadingModal.style.display = 'none';
|
179 |
+
loadingModal.classList.remove('loading-modal');
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
// إضافة إشعار
|
184 |
+
addNotification(notification) {
|
185 |
+
const newNotification = {
|
186 |
+
id: Date.now(),
|
187 |
+
timestamp: new Date().toISOString(),
|
188 |
+
read: false,
|
189 |
+
...notification
|
190 |
+
};
|
191 |
+
|
192 |
+
this.notifications.unshift(newNotification);
|
193 |
+
this.saveNotifications();
|
194 |
+
this.updateNotificationBadge();
|
195 |
+
|
196 |
+
return newNotification;
|
197 |
+
}
|
198 |
+
|
199 |
+
// وضع علامة مقروء على الإشعار
|
200 |
+
markAsRead(notificationId) {
|
201 |
+
const notification = this.notifications.find(n => n.id === notificationId);
|
202 |
+
if (notification) {
|
203 |
+
notification.read = true;
|
204 |
+
this.saveNotifications();
|
205 |
+
this.updateNotificationBadge();
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
// وضع علامة مقروء على جميع الإشعارات
|
210 |
+
markAllAsRead() {
|
211 |
+
this.notifications.forEach(n => n.read = true);
|
212 |
+
this.saveNotifications();
|
213 |
+
this.updateNotificationBadge();
|
214 |
+
}
|
215 |
+
|
216 |
+
// حذف إشعار
|
217 |
+
deleteNotification(notificationId) {
|
218 |
+
this.notifications = this.notifications.filter(n => n.id !== notificationId);
|
219 |
+
this.saveNotifications();
|
220 |
+
this.updateNotificationBadge();
|
221 |
+
}
|
222 |
+
|
223 |
+
// الحصول على الإشعارات غير المقروءة
|
224 |
+
getUnreadNotifications() {
|
225 |
+
return this.notifications.filter(n => !n.read);
|
226 |
+
}
|
227 |
+
|
228 |
+
// الحصول على عدد الإشعارات غير المقروءة
|
229 |
+
getUnreadCount() {
|
230 |
+
return this.getUnreadNotifications().length;
|
231 |
+
}
|
232 |
+
|
233 |
+
// تحديث شارة الإشعارات
|
234 |
+
updateNotificationBadge() {
|
235 |
+
const badge = document.querySelector('#notifications-btn .badge');
|
236 |
+
const unreadCount = this.getUnreadCount();
|
237 |
+
|
238 |
+
if (badge) {
|
239 |
+
if (unreadCount > 0) {
|
240 |
+
badge.textContent = unreadCount > 99 ? '99+' : unreadCount;
|
241 |
+
badge.style.display = 'block';
|
242 |
+
} else {
|
243 |
+
badge.style.display = 'none';
|
244 |
+
}
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
// عرض قائمة الإشعارات
|
249 |
+
showNotificationsList() {
|
250 |
+
const notificationsHtml = this.notifications.length > 0 ?
|
251 |
+
this.notifications.map(n => this.renderNotification(n)).join('') :
|
252 |
+
'<div class="no-notifications">لا توجد إشعارات</div>';
|
253 |
+
|
254 |
+
const content = `
|
255 |
+
<div class="notifications-header">
|
256 |
+
<button class="btn btn-link" onclick="window.notificationManager.markAllAsRead()">
|
257 |
+
وضع علامة مقروء على الكل
|
258 |
+
</button>
|
259 |
+
</div>
|
260 |
+
<div class="notifications-list">
|
261 |
+
${notificationsHtml}
|
262 |
+
</div>
|
263 |
+
`;
|
264 |
+
|
265 |
+
this.showModal('الإشعارات', content, []);
|
266 |
+
}
|
267 |
+
|
268 |
+
// عرض إشعار واحد
|
269 |
+
renderNotification(notification) {
|
270 |
+
const timeAgo = this.getTimeAgo(notification.timestamp);
|
271 |
+
const readClass = notification.read ? 'read' : 'unread';
|
272 |
+
|
273 |
+
return `
|
274 |
+
<div class="notification-item ${readClass}" data-id="${notification.id}">
|
275 |
+
<div class="notification-icon ${notification.type}">
|
276 |
+
<i class="${this.getNotificationIcon(notification.type)}"></i>
|
277 |
+
</div>
|
278 |
+
<div class="notification-content">
|
279 |
+
<h4>${notification.title}</h4>
|
280 |
+
<p>${notification.message}</p>
|
281 |
+
<span class="notification-time">${timeAgo}</span>
|
282 |
+
</div>
|
283 |
+
<div class="notification-actions">
|
284 |
+
${!notification.read ? `
|
285 |
+
<button onclick="window.notificationManager.markAsRead(${notification.id})" title="وضع علامة مقروء">
|
286 |
+
<i class="fas fa-check"></i>
|
287 |
+
</button>
|
288 |
+
` : ''}
|
289 |
+
<button onclick="window.notificationManager.deleteNotification(${notification.id})" title="حذف">
|
290 |
+
<i class="fas fa-trash"></i>
|
291 |
+
</button>
|
292 |
+
</div>
|
293 |
+
</div>
|
294 |
+
`;
|
295 |
+
}
|
296 |
+
|
297 |
+
// الحصول على أيقونة الإشعار
|
298 |
+
getNotificationIcon(type) {
|
299 |
+
const icons = {
|
300 |
+
transaction: 'fas fa-exchange-alt',
|
301 |
+
security: 'fas fa-shield-alt',
|
302 |
+
system: 'fas fa-cog',
|
303 |
+
promotion: 'fas fa-gift',
|
304 |
+
warning: 'fas fa-exclamation-triangle'
|
305 |
+
};
|
306 |
+
return icons[type] || 'fas fa-bell';
|
307 |
+
}
|
308 |
+
|
309 |
+
// حساب الوقت المنقضي
|
310 |
+
getTimeAgo(timestamp) {
|
311 |
+
const now = new Date();
|
312 |
+
const time = new Date(timestamp);
|
313 |
+
const diffInSeconds = Math.floor((now - time) / 1000);
|
314 |
+
|
315 |
+
if (diffInSeconds < 60) return 'الآن';
|
316 |
+
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} دقيقة`;
|
317 |
+
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} ساعة`;
|
318 |
+
if (diffInSeconds < 2592000) return `${Math.floor(diffInSeconds / 86400)} يوم`;
|
319 |
+
return time.toLocaleDateString('ar-SA');
|
320 |
+
}
|
321 |
+
|
322 |
+
// حفظ الإشعارات
|
323 |
+
saveNotifications() {
|
324 |
+
localStorage.setItem('unifiedWallet_notifications', JSON.stringify(this.notifications));
|
325 |
+
}
|
326 |
+
|
327 |
+
// تحميل الإشعارات المحفوظة
|
328 |
+
loadStoredNotifications() {
|
329 |
+
const stored = localStorage.getItem('unifiedWallet_notifications');
|
330 |
+
if (stored) {
|
331 |
+
this.notifications = JSON.parse(stored);
|
332 |
+
this.updateNotificationBadge();
|
333 |
+
}
|
334 |
+
}
|
335 |
+
|
336 |
+
// إضافة إشعار معاملة
|
337 |
+
addTransactionNotification(type, amount, wallet) {
|
338 |
+
const messages = {
|
339 |
+
send: `تم إرسال ${amount.toLocaleString()} ر.ي من ${wallet}`,
|
340 |
+
receive: `تم استلام ${amount.toLocaleString()} ر.ي في ${wallet}`,
|
341 |
+
bill: `تم دفع فاتورة بمبلغ ${amount.toLocaleString()} ر.ي من ${wallet}`
|
342 |
+
};
|
343 |
+
|
344 |
+
this.addNotification({
|
345 |
+
type: 'transaction',
|
346 |
+
title: 'معاملة جديدة',
|
347 |
+
message: messages[type] || `معاملة بمبلغ ${amount.toLocaleString()} ر.ي`
|
348 |
+
});
|
349 |
+
}
|
350 |
+
|
351 |
+
// إضافة إشعار أمني
|
352 |
+
addSecurityNotification(message) {
|
353 |
+
this.addNotification({
|
354 |
+
type: 'security',
|
355 |
+
title: 'تنبيه أمني',
|
356 |
+
message: message
|
357 |
+
});
|
358 |
+
}
|
359 |
+
|
360 |
+
// إضافة إشعار نظام
|
361 |
+
addSystemNotification(message) {
|
362 |
+
this.addNotification({
|
363 |
+
type: 'system',
|
364 |
+
title: 'إشعار النظام',
|
365 |
+
message: message
|
366 |
+
});
|
367 |
+
}
|
368 |
+
}
|
369 |
+
|
370 |
+
// تصدير الكلاس للاستخدام في التطبيق الرئيسي
|
371 |
+
window.NotificationManager = NotificationManager;
|
package-lock.json
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
package-new.json
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "unified-wallet-app",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية الموحد",
|
5 |
+
"author": "المدى للخدمات البرمجية التسويقية والإعلانية",
|
6 |
+
"homepage": "https://almada.com/",
|
7 |
+
"main": "index.html",
|
8 |
+
"scripts": {
|
9 |
+
"start": "http-server . -p 8100",
|
10 |
+
"build": "echo 'Building for production...'",
|
11 |
+
"cap:add:android": "cap add android",
|
12 |
+
"cap:build:android": "cap copy android && cap open android",
|
13 |
+
"cap:sync": "cap sync",
|
14 |
+
"serve": "http-server . -p 8100 -o"
|
15 |
+
},
|
16 |
+
"dependencies": {
|
17 |
+
"@capacitor/android": "^4.8.0",
|
18 |
+
"@capacitor/app": "^4.1.1",
|
19 |
+
"@capacitor/core": "^4.8.0",
|
20 |
+
"@capacitor/device": "^4.1.0",
|
21 |
+
"@capacitor/haptics": "^4.1.0",
|
22 |
+
"@capacitor/keyboard": "^4.1.1",
|
23 |
+
"@capacitor/local-notifications": "^4.1.5",
|
24 |
+
"@capacitor/splash-screen": "^4.2.0",
|
25 |
+
"@capacitor/status-bar": "^4.2.0",
|
26 |
+
"@capacitor/storage": "^1.2.5"
|
27 |
+
},
|
28 |
+
"devDependencies": {
|
29 |
+
"@capacitor/cli": "^4.8.0",
|
30 |
+
"http-server": "^14.1.1"
|
31 |
+
},
|
32 |
+
"keywords": [
|
33 |
+
"wallet",
|
34 |
+
"yemen",
|
35 |
+
"fintech",
|
36 |
+
"mobile",
|
37 |
+
"محفظة",
|
38 |
+
"يمن"
|
39 |
+
],
|
40 |
+
"license": "MIT"
|
41 |
+
}
|
package-simple.json
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "unified-wallet-app",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية الموحد",
|
5 |
+
"author": "المدى للخدمات البرمجية التسويقية والإعلانية",
|
6 |
+
"homepage": "https://almada.com/",
|
7 |
+
"main": "index.html",
|
8 |
+
"scripts": {
|
9 |
+
"start": "http-server . -p 8100",
|
10 |
+
"build": "echo 'Building for production...'",
|
11 |
+
"cap:init": "cap init",
|
12 |
+
"cap:add:android": "cap add android",
|
13 |
+
"cap:build:android": "cap copy android && cap open android",
|
14 |
+
"cap:sync": "cap sync",
|
15 |
+
"serve": "http-server . -p 8100 -o"
|
16 |
+
},
|
17 |
+
"dependencies": {
|
18 |
+
"@capacitor/android": "^4.8.0",
|
19 |
+
"@capacitor/app": "^4.1.1",
|
20 |
+
"@capacitor/core": "^4.8.0",
|
21 |
+
"@capacitor/device": "^4.1.0",
|
22 |
+
"@capacitor/haptics": "^4.1.0",
|
23 |
+
"@capacitor/keyboard": "^4.1.1",
|
24 |
+
"@capacitor/local-notifications": "^4.1.5",
|
25 |
+
"@capacitor/splash-screen": "^4.2.0",
|
26 |
+
"@capacitor/status-bar": "^4.2.0",
|
27 |
+
"@capacitor/storage": "^1.2.5"
|
28 |
+
},
|
29 |
+
"devDependencies": {
|
30 |
+
"@capacitor/cli": "^4.8.0",
|
31 |
+
"http-server": "^14.1.1"
|
32 |
+
},
|
33 |
+
"keywords": [
|
34 |
+
"wallet",
|
35 |
+
"yemen",
|
36 |
+
"fintech",
|
37 |
+
"mobile",
|
38 |
+
"محفظة",
|
39 |
+
"يمن"
|
40 |
+
],
|
41 |
+
"license": "MIT"
|
42 |
+
}
|
package.json
CHANGED
@@ -1,64 +1,86 @@
|
|
1 |
{
|
2 |
-
"name": "
|
3 |
-
"version": "
|
4 |
-
"
|
|
|
|
|
5 |
"scripts": {
|
6 |
-
"
|
7 |
-
"build": "
|
8 |
-
"
|
9 |
-
"
|
|
|
|
|
|
|
10 |
},
|
|
|
11 |
"dependencies": {
|
12 |
-
"@
|
13 |
-
"@
|
14 |
-
"@
|
15 |
-
"@
|
16 |
-
"@
|
17 |
-
"@
|
18 |
-
"@
|
19 |
-
"@
|
20 |
-
"@
|
21 |
-
"@
|
22 |
-
"@
|
23 |
-
"@
|
24 |
-
"@
|
25 |
-
"@
|
26 |
-
"@
|
27 |
-
"@
|
28 |
-
"@
|
29 |
-
"@
|
30 |
-
"@
|
31 |
-
"
|
32 |
-
"
|
33 |
-
"
|
34 |
-
"
|
35 |
-
"
|
36 |
-
|
37 |
-
"
|
38 |
-
"
|
39 |
-
"
|
40 |
-
"
|
41 |
-
"react": "^19.0.0",
|
42 |
-
"react-dom": "^19.0.0",
|
43 |
-
"react-icons": "^5.5.0",
|
44 |
-
"react-use": "^17.6.0",
|
45 |
-
"redaxios": "^0.5.1",
|
46 |
-
"sonner": "^2.0.5",
|
47 |
-
"tailwind-merge": "^3.3.0",
|
48 |
-
"zod": "^3.25.57"
|
49 |
},
|
50 |
"devDependencies": {
|
51 |
-
"@
|
52 |
-
"@
|
53 |
-
"@
|
54 |
-
"@
|
55 |
-
"@
|
56 |
-
"eslint": "^
|
57 |
-
"
|
58 |
-
"
|
59 |
-
"
|
60 |
-
"
|
61 |
-
"
|
62 |
-
"
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
}
|
|
|
1 |
{
|
2 |
+
"name": "unified-wallet-app",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "محفظتي الموحدة - تطبيق المحافظ الإلكترونية اليمنية الموحد",
|
5 |
+
"author": "المدى للخدمات البرمجية التسويقية والإعلانية",
|
6 |
+
"homepage": "https://almada.com/",
|
7 |
"scripts": {
|
8 |
+
"start": "ionic serve",
|
9 |
+
"build": "ionic build",
|
10 |
+
"build:prod": "ionic build --prod",
|
11 |
+
"cap:add:android": "ionic cap add android",
|
12 |
+
"cap:build:android": "ionic cap build android",
|
13 |
+
"cap:open:android": "ionic cap open android",
|
14 |
+
"cap:sync": "ionic cap sync"
|
15 |
},
|
16 |
+
"private": true,
|
17 |
"dependencies": {
|
18 |
+
"@angular/animations": "^17.0.0",
|
19 |
+
"@angular/common": "^17.0.0",
|
20 |
+
"@angular/compiler": "^17.0.0",
|
21 |
+
"@angular/core": "^17.0.0",
|
22 |
+
"@angular/forms": "^17.0.0",
|
23 |
+
"@angular/platform-browser": "^17.0.0",
|
24 |
+
"@angular/platform-browser-dynamic": "^17.0.0",
|
25 |
+
"@angular/router": "^17.0.0",
|
26 |
+
"@angular/service-worker": "^17.0.0",
|
27 |
+
"@capacitor/android": "^5.0.0",
|
28 |
+
"@capacitor/app": "^5.0.0",
|
29 |
+
"@capacitor/camera": "^5.0.0",
|
30 |
+
"@capacitor/core": "^5.0.0",
|
31 |
+
"@capacitor/device": "^5.0.0",
|
32 |
+
"@capacitor/haptics": "^5.0.0",
|
33 |
+
"@capacitor/ios": "^5.0.0",
|
34 |
+
"@capacitor/keyboard": "^5.0.0",
|
35 |
+
"@capacitor/local-notifications": "^5.0.0",
|
36 |
+
"@capacitor/push-notifications": "^5.0.0",
|
37 |
+
"@capacitor/splash-screen": "^5.0.0",
|
38 |
+
"@capacitor/status-bar": "^5.0.0",
|
39 |
+
"@capacitor/preferences": "^4.0.0",
|
40 |
+
"@ionic/angular": "^7.0.0",
|
41 |
+
"@ionic/storage-angular": "^4.0.0",
|
42 |
+
|
43 |
+
"ionicons": "^7.0.0",
|
44 |
+
"rxjs": "~7.8.0",
|
45 |
+
"tslib": "^2.3.0",
|
46 |
+
"zone.js": "~0.14.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
},
|
48 |
"devDependencies": {
|
49 |
+
"@angular-devkit/build-angular": "^17.0.0",
|
50 |
+
"@angular-eslint/builder": "^17.0.0",
|
51 |
+
"@angular-eslint/eslint-plugin": "^17.0.0",
|
52 |
+
"@angular-eslint/eslint-plugin-template": "^17.0.0",
|
53 |
+
"@angular-eslint/schematics": "^17.0.0",
|
54 |
+
"@angular-eslint/template-parser": "^17.0.0",
|
55 |
+
"@angular/cli": "^17.0.0",
|
56 |
+
"@angular/compiler-cli": "^17.0.0",
|
57 |
+
"@angular/language-service": "^17.0.0",
|
58 |
+
"@capacitor/cli": "^5.0.0",
|
59 |
+
"@ionic/angular-toolkit": "^9.0.0",
|
60 |
+
"@types/jasmine": "~5.1.0",
|
61 |
+
"@types/node": "^18.18.0",
|
62 |
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
63 |
+
"@typescript-eslint/parser": "^6.0.0",
|
64 |
+
"eslint": "^8.57.0",
|
65 |
+
"eslint-plugin-import": "^2.29.0",
|
66 |
+
"eslint-plugin-jsdoc": "^46.8.0",
|
67 |
+
"eslint-plugin-prefer-arrow": "^1.2.2",
|
68 |
+
"jasmine-core": "~5.1.0",
|
69 |
+
"karma": "~6.4.0",
|
70 |
+
"karma-chrome-launcher": "~3.2.0",
|
71 |
+
"karma-coverage": "~2.2.0",
|
72 |
+
"karma-jasmine": "~5.1.0",
|
73 |
+
"karma-jasmine-html-reporter": "~2.1.0",
|
74 |
+
"typescript": "~5.2.0"
|
75 |
+
},
|
76 |
+
"keywords": [
|
77 |
+
"ionic",
|
78 |
+
"angular",
|
79 |
+
"mobile",
|
80 |
+
"wallet",
|
81 |
+
"yemen",
|
82 |
+
"fintech",
|
83 |
+
"محفظة",
|
84 |
+
"يمن"
|
85 |
+
]
|
86 |
}
|
styles.css
ADDED
@@ -0,0 +1,1334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* المتغيرات العامة */
|
2 |
+
:root {
|
3 |
+
--primary-color: #2563eb;
|
4 |
+
--secondary-color: #1e40af;
|
5 |
+
--success-color: #059669;
|
6 |
+
--warning-color: #d97706;
|
7 |
+
--error-color: #dc2626;
|
8 |
+
--text-primary: #1f2937;
|
9 |
+
--text-secondary: #6b7280;
|
10 |
+
--background: #f8fafc;
|
11 |
+
--surface: #ffffff;
|
12 |
+
--border: #e5e7eb;
|
13 |
+
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
14 |
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
15 |
+
--border-radius: 12px;
|
16 |
+
--transition: all 0.3s ease;
|
17 |
+
}
|
18 |
+
|
19 |
+
/* إعادة تعيين الأساسيات */
|
20 |
+
* {
|
21 |
+
margin: 0;
|
22 |
+
padding: 0;
|
23 |
+
box-sizing: border-box;
|
24 |
+
}
|
25 |
+
|
26 |
+
body {
|
27 |
+
font-family: 'Tajawal', sans-serif;
|
28 |
+
background: var(--background);
|
29 |
+
color: var(--text-primary);
|
30 |
+
line-height: 1.6;
|
31 |
+
overflow-x: hidden;
|
32 |
+
}
|
33 |
+
|
34 |
+
/* الشاشات */
|
35 |
+
.screen {
|
36 |
+
display: none;
|
37 |
+
min-height: 100vh;
|
38 |
+
position: relative;
|
39 |
+
}
|
40 |
+
|
41 |
+
.screen.active {
|
42 |
+
display: block;
|
43 |
+
}
|
44 |
+
|
45 |
+
/* شاشة التحميل */
|
46 |
+
.loading-screen {
|
47 |
+
position: fixed;
|
48 |
+
top: 0;
|
49 |
+
left: 0;
|
50 |
+
width: 100%;
|
51 |
+
height: 100%;
|
52 |
+
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
53 |
+
display: flex;
|
54 |
+
align-items: center;
|
55 |
+
justify-content: center;
|
56 |
+
z-index: 9999;
|
57 |
+
color: white;
|
58 |
+
}
|
59 |
+
|
60 |
+
.loading-content {
|
61 |
+
text-align: center;
|
62 |
+
animation: fadeInUp 0.8s ease;
|
63 |
+
}
|
64 |
+
|
65 |
+
.loading-logo {
|
66 |
+
font-size: 4rem;
|
67 |
+
margin-bottom: 1rem;
|
68 |
+
animation: pulse 2s infinite;
|
69 |
+
}
|
70 |
+
|
71 |
+
.loading-spinner {
|
72 |
+
width: 40px;
|
73 |
+
height: 40px;
|
74 |
+
border: 3px solid rgba(255, 255, 255, 0.3);
|
75 |
+
border-top: 3px solid white;
|
76 |
+
border-radius: 50%;
|
77 |
+
animation: spin 1s linear infinite;
|
78 |
+
margin: 1rem auto;
|
79 |
+
}
|
80 |
+
|
81 |
+
/* شاشة تسجيل الدخول */
|
82 |
+
.login-container {
|
83 |
+
max-width: 400px;
|
84 |
+
margin: 0 auto;
|
85 |
+
padding: 2rem;
|
86 |
+
min-height: 100vh;
|
87 |
+
display: flex;
|
88 |
+
flex-direction: column;
|
89 |
+
justify-content: center;
|
90 |
+
}
|
91 |
+
|
92 |
+
.login-header {
|
93 |
+
text-align: center;
|
94 |
+
margin-bottom: 2rem;
|
95 |
+
}
|
96 |
+
|
97 |
+
.app-logo {
|
98 |
+
font-size: 3rem;
|
99 |
+
color: var(--primary-color);
|
100 |
+
margin-bottom: 1rem;
|
101 |
+
}
|
102 |
+
|
103 |
+
.login-header h1 {
|
104 |
+
font-size: 1.8rem;
|
105 |
+
font-weight: 700;
|
106 |
+
margin-bottom: 0.5rem;
|
107 |
+
}
|
108 |
+
|
109 |
+
.login-header p {
|
110 |
+
color: var(--text-secondary);
|
111 |
+
font-size: 0.9rem;
|
112 |
+
}
|
113 |
+
|
114 |
+
/* النماذج */
|
115 |
+
.form-group {
|
116 |
+
margin-bottom: 1.5rem;
|
117 |
+
}
|
118 |
+
|
119 |
+
.form-group label {
|
120 |
+
display: block;
|
121 |
+
margin-bottom: 0.5rem;
|
122 |
+
font-weight: 500;
|
123 |
+
color: var(--text-primary);
|
124 |
+
}
|
125 |
+
|
126 |
+
.input-group {
|
127 |
+
position: relative;
|
128 |
+
display: flex;
|
129 |
+
}
|
130 |
+
|
131 |
+
.input-prefix {
|
132 |
+
background: var(--border);
|
133 |
+
padding: 0.75rem 1rem;
|
134 |
+
border: 1px solid var(--border);
|
135 |
+
border-left: none;
|
136 |
+
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
137 |
+
color: var(--text-secondary);
|
138 |
+
font-weight: 500;
|
139 |
+
}
|
140 |
+
|
141 |
+
input {
|
142 |
+
width: 100%;
|
143 |
+
padding: 0.75rem 1rem;
|
144 |
+
border: 1px solid var(--border);
|
145 |
+
border-radius: var(--border-radius);
|
146 |
+
font-family: inherit;
|
147 |
+
font-size: 1rem;
|
148 |
+
transition: var(--transition);
|
149 |
+
}
|
150 |
+
|
151 |
+
.input-group input {
|
152 |
+
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
153 |
+
border-right: none;
|
154 |
+
}
|
155 |
+
|
156 |
+
input:focus {
|
157 |
+
outline: none;
|
158 |
+
border-color: var(--primary-color);
|
159 |
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
160 |
+
}
|
161 |
+
|
162 |
+
/* الأزرار */
|
163 |
+
.btn-primary, .btn-secondary, .btn-small, .btn-link {
|
164 |
+
padding: 0.75rem 1.5rem;
|
165 |
+
border: none;
|
166 |
+
border-radius: var(--border-radius);
|
167 |
+
font-family: inherit;
|
168 |
+
font-size: 1rem;
|
169 |
+
font-weight: 500;
|
170 |
+
cursor: pointer;
|
171 |
+
transition: var(--transition);
|
172 |
+
display: inline-flex;
|
173 |
+
align-items: center;
|
174 |
+
justify-content: center;
|
175 |
+
gap: 0.5rem;
|
176 |
+
text-decoration: none;
|
177 |
+
}
|
178 |
+
|
179 |
+
.btn-primary {
|
180 |
+
background: var(--primary-color);
|
181 |
+
color: white;
|
182 |
+
width: 100%;
|
183 |
+
margin-bottom: 1rem;
|
184 |
+
}
|
185 |
+
|
186 |
+
.btn-primary:hover {
|
187 |
+
background: var(--secondary-color);
|
188 |
+
transform: translateY(-2px);
|
189 |
+
box-shadow: var(--shadow);
|
190 |
+
}
|
191 |
+
|
192 |
+
.btn-secondary {
|
193 |
+
background: var(--surface);
|
194 |
+
color: var(--text-primary);
|
195 |
+
border: 1px solid var(--border);
|
196 |
+
width: 100%;
|
197 |
+
}
|
198 |
+
|
199 |
+
.btn-secondary:hover {
|
200 |
+
background: var(--background);
|
201 |
+
}
|
202 |
+
|
203 |
+
.btn-small {
|
204 |
+
padding: 0.5rem 1rem;
|
205 |
+
font-size: 0.875rem;
|
206 |
+
background: var(--surface);
|
207 |
+
color: var(--text-primary);
|
208 |
+
border: 1px solid var(--border);
|
209 |
+
}
|
210 |
+
|
211 |
+
.btn-link {
|
212 |
+
background: none;
|
213 |
+
color: var(--primary-color);
|
214 |
+
padding: 0;
|
215 |
+
font-size: 0.875rem;
|
216 |
+
}
|
217 |
+
|
218 |
+
/* تسجيل الدخول البديل */
|
219 |
+
.alternative-login {
|
220 |
+
text-align: center;
|
221 |
+
margin: 1rem 0;
|
222 |
+
}
|
223 |
+
|
224 |
+
.alternative-login p {
|
225 |
+
color: var(--text-secondary);
|
226 |
+
margin-bottom: 1rem;
|
227 |
+
position: relative;
|
228 |
+
}
|
229 |
+
|
230 |
+
.alternative-login p::before,
|
231 |
+
.alternative-login p::after {
|
232 |
+
content: '';
|
233 |
+
position: absolute;
|
234 |
+
top: 50%;
|
235 |
+
width: 40%;
|
236 |
+
height: 1px;
|
237 |
+
background: var(--border);
|
238 |
+
}
|
239 |
+
|
240 |
+
.alternative-login p::before {
|
241 |
+
right: 60%;
|
242 |
+
}
|
243 |
+
|
244 |
+
.alternative-login p::after {
|
245 |
+
left: 60%;
|
246 |
+
}
|
247 |
+
|
248 |
+
.register-link {
|
249 |
+
text-align: center;
|
250 |
+
margin-top: 2rem;
|
251 |
+
}
|
252 |
+
|
253 |
+
.register-link a {
|
254 |
+
color: var(--primary-color);
|
255 |
+
text-decoration: none;
|
256 |
+
}
|
257 |
+
|
258 |
+
/* المحافظ المدعومة */
|
259 |
+
.supported-wallets {
|
260 |
+
margin-top: 2rem;
|
261 |
+
text-align: center;
|
262 |
+
}
|
263 |
+
|
264 |
+
.supported-wallets h3 {
|
265 |
+
font-size: 1rem;
|
266 |
+
margin-bottom: 1rem;
|
267 |
+
color: var(--text-secondary);
|
268 |
+
}
|
269 |
+
|
270 |
+
.wallet-logos {
|
271 |
+
display: grid;
|
272 |
+
grid-template-columns: repeat(3, 1fr);
|
273 |
+
gap: 1rem;
|
274 |
+
}
|
275 |
+
|
276 |
+
.wallet-logo {
|
277 |
+
width: 60px;
|
278 |
+
height: 60px;
|
279 |
+
border-radius: var(--border-radius);
|
280 |
+
overflow: hidden;
|
281 |
+
border: 1px solid var(--border);
|
282 |
+
display: flex;
|
283 |
+
align-items: center;
|
284 |
+
justify-content: center;
|
285 |
+
background: var(--surface);
|
286 |
+
transition: var(--transition);
|
287 |
+
}
|
288 |
+
|
289 |
+
.wallet-logo:hover {
|
290 |
+
transform: scale(1.05);
|
291 |
+
box-shadow: var(--shadow);
|
292 |
+
}
|
293 |
+
|
294 |
+
.wallet-logo img {
|
295 |
+
width: 100%;
|
296 |
+
height: 100%;
|
297 |
+
object-fit: cover;
|
298 |
+
}
|
299 |
+
|
300 |
+
/* الشاشة الرئيسية */
|
301 |
+
.main-header {
|
302 |
+
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
303 |
+
color: white;
|
304 |
+
padding: 2rem 1rem;
|
305 |
+
border-radius: 0 0 2rem 2rem;
|
306 |
+
}
|
307 |
+
|
308 |
+
.header-top {
|
309 |
+
display: flex;
|
310 |
+
justify-content: space-between;
|
311 |
+
align-items: center;
|
312 |
+
margin-bottom: 2rem;
|
313 |
+
}
|
314 |
+
|
315 |
+
.user-info {
|
316 |
+
display: flex;
|
317 |
+
align-items: center;
|
318 |
+
gap: 1rem;
|
319 |
+
}
|
320 |
+
|
321 |
+
.user-avatar {
|
322 |
+
width: 50px;
|
323 |
+
height: 50px;
|
324 |
+
background: rgba(255, 255, 255, 0.2);
|
325 |
+
border-radius: 50%;
|
326 |
+
display: flex;
|
327 |
+
align-items: center;
|
328 |
+
justify-content: center;
|
329 |
+
font-size: 1.5rem;
|
330 |
+
}
|
331 |
+
|
332 |
+
.user-details h3 {
|
333 |
+
font-size: 1.1rem;
|
334 |
+
margin-bottom: 0.25rem;
|
335 |
+
}
|
336 |
+
|
337 |
+
.user-details p {
|
338 |
+
font-size: 0.875rem;
|
339 |
+
opacity: 0.8;
|
340 |
+
}
|
341 |
+
|
342 |
+
.header-actions {
|
343 |
+
display: flex;
|
344 |
+
gap: 0.5rem;
|
345 |
+
}
|
346 |
+
|
347 |
+
.icon-btn {
|
348 |
+
width: 40px;
|
349 |
+
height: 40px;
|
350 |
+
background: rgba(255, 255, 255, 0.2);
|
351 |
+
border: none;
|
352 |
+
border-radius: 50%;
|
353 |
+
color: white;
|
354 |
+
cursor: pointer;
|
355 |
+
display: flex;
|
356 |
+
align-items: center;
|
357 |
+
justify-content: center;
|
358 |
+
position: relative;
|
359 |
+
transition: var(--transition);
|
360 |
+
}
|
361 |
+
|
362 |
+
.icon-btn:hover {
|
363 |
+
background: rgba(255, 255, 255, 0.3);
|
364 |
+
}
|
365 |
+
|
366 |
+
.badge {
|
367 |
+
position: absolute;
|
368 |
+
top: -5px;
|
369 |
+
left: -5px;
|
370 |
+
background: var(--error-color);
|
371 |
+
color: white;
|
372 |
+
font-size: 0.75rem;
|
373 |
+
padding: 0.125rem 0.375rem;
|
374 |
+
border-radius: 10px;
|
375 |
+
min-width: 18px;
|
376 |
+
text-align: center;
|
377 |
+
}
|
378 |
+
|
379 |
+
/* الرصيد الإجمالي */
|
380 |
+
.total-balance {
|
381 |
+
text-align: center;
|
382 |
+
}
|
383 |
+
|
384 |
+
.total-balance h2 {
|
385 |
+
font-size: 1rem;
|
386 |
+
opacity: 0.9;
|
387 |
+
margin-bottom: 0.5rem;
|
388 |
+
}
|
389 |
+
|
390 |
+
.balance-amount {
|
391 |
+
font-size: 2.5rem;
|
392 |
+
font-weight: 700;
|
393 |
+
margin-bottom: 1rem;
|
394 |
+
}
|
395 |
+
|
396 |
+
.balance-actions {
|
397 |
+
display: flex;
|
398 |
+
justify-content: center;
|
399 |
+
gap: 1rem;
|
400 |
+
}
|
401 |
+
|
402 |
+
.balance-actions .btn-small {
|
403 |
+
background: rgba(255, 255, 255, 0.2);
|
404 |
+
color: white;
|
405 |
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
406 |
+
}
|
407 |
+
|
408 |
+
/* المحتوى الرئيسي */
|
409 |
+
.main-content {
|
410 |
+
padding: 2rem 1rem 6rem;
|
411 |
+
}
|
412 |
+
|
413 |
+
.main-content section {
|
414 |
+
margin-bottom: 2rem;
|
415 |
+
}
|
416 |
+
|
417 |
+
.section-header {
|
418 |
+
display: flex;
|
419 |
+
justify-content: space-between;
|
420 |
+
align-items: center;
|
421 |
+
margin-bottom: 1rem;
|
422 |
+
}
|
423 |
+
|
424 |
+
.section-header h3 {
|
425 |
+
font-size: 1.25rem;
|
426 |
+
font-weight: 600;
|
427 |
+
}
|
428 |
+
|
429 |
+
/* الإجراءات السريعة */
|
430 |
+
.actions-grid {
|
431 |
+
display: grid;
|
432 |
+
grid-template-columns: repeat(2, 1fr);
|
433 |
+
gap: 1rem;
|
434 |
+
}
|
435 |
+
|
436 |
+
.action-btn {
|
437 |
+
background: var(--surface);
|
438 |
+
border: 1px solid var(--border);
|
439 |
+
border-radius: var(--border-radius);
|
440 |
+
padding: 1.5rem;
|
441 |
+
display: flex;
|
442 |
+
flex-direction: column;
|
443 |
+
align-items: center;
|
444 |
+
gap: 0.5rem;
|
445 |
+
cursor: pointer;
|
446 |
+
transition: var(--transition);
|
447 |
+
text-decoration: none;
|
448 |
+
color: var(--text-primary);
|
449 |
+
}
|
450 |
+
|
451 |
+
.action-btn:hover {
|
452 |
+
transform: translateY(-2px);
|
453 |
+
box-shadow: var(--shadow);
|
454 |
+
border-color: var(--primary-color);
|
455 |
+
}
|
456 |
+
|
457 |
+
.action-btn i {
|
458 |
+
font-size: 1.5rem;
|
459 |
+
color: var(--primary-color);
|
460 |
+
}
|
461 |
+
|
462 |
+
.action-btn span {
|
463 |
+
font-weight: 500;
|
464 |
+
}
|
465 |
+
|
466 |
+
/* الرسوم المتحركة */
|
467 |
+
@keyframes fadeInUp {
|
468 |
+
from {
|
469 |
+
opacity: 0;
|
470 |
+
transform: translateY(30px);
|
471 |
+
}
|
472 |
+
to {
|
473 |
+
opacity: 1;
|
474 |
+
transform: translateY(0);
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
@keyframes pulse {
|
479 |
+
0%, 100% {
|
480 |
+
transform: scale(1);
|
481 |
+
}
|
482 |
+
50% {
|
483 |
+
transform: scale(1.1);
|
484 |
+
}
|
485 |
+
}
|
486 |
+
|
487 |
+
@keyframes spin {
|
488 |
+
from {
|
489 |
+
transform: rotate(0deg);
|
490 |
+
}
|
491 |
+
to {
|
492 |
+
transform: rotate(360deg);
|
493 |
+
}
|
494 |
+
}
|
495 |
+
|
496 |
+
/* قائمة المحافظ */
|
497 |
+
.wallets-list {
|
498 |
+
display: flex;
|
499 |
+
flex-direction: column;
|
500 |
+
gap: 1rem;
|
501 |
+
}
|
502 |
+
|
503 |
+
.wallet-card {
|
504 |
+
background: var(--surface);
|
505 |
+
border: 1px solid var(--border);
|
506 |
+
border-radius: var(--border-radius);
|
507 |
+
padding: 1.5rem;
|
508 |
+
display: flex;
|
509 |
+
align-items: center;
|
510 |
+
justify-content: space-between;
|
511 |
+
transition: var(--transition);
|
512 |
+
cursor: pointer;
|
513 |
+
}
|
514 |
+
|
515 |
+
.wallet-card:hover {
|
516 |
+
transform: translateY(-2px);
|
517 |
+
box-shadow: var(--shadow);
|
518 |
+
border-color: var(--primary-color);
|
519 |
+
}
|
520 |
+
|
521 |
+
.wallet-info {
|
522 |
+
display: flex;
|
523 |
+
align-items: center;
|
524 |
+
gap: 1rem;
|
525 |
+
}
|
526 |
+
|
527 |
+
.wallet-icon {
|
528 |
+
width: 50px;
|
529 |
+
height: 50px;
|
530 |
+
border-radius: var(--border-radius);
|
531 |
+
overflow: hidden;
|
532 |
+
border: 1px solid var(--border);
|
533 |
+
display: flex;
|
534 |
+
align-items: center;
|
535 |
+
justify-content: center;
|
536 |
+
}
|
537 |
+
|
538 |
+
.wallet-icon img {
|
539 |
+
width: 100%;
|
540 |
+
height: 100%;
|
541 |
+
object-fit: cover;
|
542 |
+
}
|
543 |
+
|
544 |
+
.wallet-details h4 {
|
545 |
+
font-size: 1.1rem;
|
546 |
+
font-weight: 600;
|
547 |
+
margin-bottom: 0.25rem;
|
548 |
+
}
|
549 |
+
|
550 |
+
.wallet-details p {
|
551 |
+
color: var(--text-secondary);
|
552 |
+
font-size: 0.875rem;
|
553 |
+
}
|
554 |
+
|
555 |
+
.wallet-balance {
|
556 |
+
text-align: left;
|
557 |
+
}
|
558 |
+
|
559 |
+
.wallet-balance .amount {
|
560 |
+
font-size: 1.25rem;
|
561 |
+
font-weight: 700;
|
562 |
+
color: var(--success-color);
|
563 |
+
}
|
564 |
+
|
565 |
+
.wallet-balance .currency {
|
566 |
+
font-size: 0.875rem;
|
567 |
+
color: var(--text-secondary);
|
568 |
+
}
|
569 |
+
|
570 |
+
/* قائمة المعاملات */
|
571 |
+
.transactions-list {
|
572 |
+
display: flex;
|
573 |
+
flex-direction: column;
|
574 |
+
gap: 0.75rem;
|
575 |
+
}
|
576 |
+
|
577 |
+
.transaction-item {
|
578 |
+
background: var(--surface);
|
579 |
+
border: 1px solid var(--border);
|
580 |
+
border-radius: var(--border-radius);
|
581 |
+
padding: 1rem;
|
582 |
+
display: flex;
|
583 |
+
align-items: center;
|
584 |
+
justify-content: space-between;
|
585 |
+
}
|
586 |
+
|
587 |
+
.transaction-info {
|
588 |
+
display: flex;
|
589 |
+
align-items: center;
|
590 |
+
gap: 1rem;
|
591 |
+
}
|
592 |
+
|
593 |
+
.transaction-icon {
|
594 |
+
width: 40px;
|
595 |
+
height: 40px;
|
596 |
+
border-radius: 50%;
|
597 |
+
display: flex;
|
598 |
+
align-items: center;
|
599 |
+
justify-content: center;
|
600 |
+
font-size: 1rem;
|
601 |
+
}
|
602 |
+
|
603 |
+
.transaction-icon.send {
|
604 |
+
background: rgba(220, 38, 38, 0.1);
|
605 |
+
color: var(--error-color);
|
606 |
+
}
|
607 |
+
|
608 |
+
.transaction-icon.receive {
|
609 |
+
background: rgba(5, 150, 105, 0.1);
|
610 |
+
color: var(--success-color);
|
611 |
+
}
|
612 |
+
|
613 |
+
.transaction-icon.bill {
|
614 |
+
background: rgba(217, 119, 6, 0.1);
|
615 |
+
color: var(--warning-color);
|
616 |
+
}
|
617 |
+
|
618 |
+
.transaction-details h5 {
|
619 |
+
font-size: 1rem;
|
620 |
+
font-weight: 500;
|
621 |
+
margin-bottom: 0.25rem;
|
622 |
+
}
|
623 |
+
|
624 |
+
.transaction-details p {
|
625 |
+
color: var(--text-secondary);
|
626 |
+
font-size: 0.875rem;
|
627 |
+
}
|
628 |
+
|
629 |
+
.transaction-amount {
|
630 |
+
text-align: left;
|
631 |
+
}
|
632 |
+
|
633 |
+
.transaction-amount .amount {
|
634 |
+
font-size: 1rem;
|
635 |
+
font-weight: 600;
|
636 |
+
}
|
637 |
+
|
638 |
+
.transaction-amount.positive .amount {
|
639 |
+
color: var(--success-color);
|
640 |
+
}
|
641 |
+
|
642 |
+
.transaction-amount.negative .amount {
|
643 |
+
color: var(--error-color);
|
644 |
+
}
|
645 |
+
|
646 |
+
.transaction-amount .time {
|
647 |
+
font-size: 0.75rem;
|
648 |
+
color: var(--text-secondary);
|
649 |
+
}
|
650 |
+
|
651 |
+
/* شريط التنقل السفلي */
|
652 |
+
.bottom-nav {
|
653 |
+
position: fixed;
|
654 |
+
bottom: 0;
|
655 |
+
left: 0;
|
656 |
+
right: 0;
|
657 |
+
background: var(--surface);
|
658 |
+
border-top: 1px solid var(--border);
|
659 |
+
display: flex;
|
660 |
+
justify-content: space-around;
|
661 |
+
padding: 0.75rem 0;
|
662 |
+
z-index: 100;
|
663 |
+
}
|
664 |
+
|
665 |
+
.nav-item {
|
666 |
+
background: none;
|
667 |
+
border: none;
|
668 |
+
display: flex;
|
669 |
+
flex-direction: column;
|
670 |
+
align-items: center;
|
671 |
+
gap: 0.25rem;
|
672 |
+
cursor: pointer;
|
673 |
+
color: var(--text-secondary);
|
674 |
+
transition: var(--transition);
|
675 |
+
padding: 0.5rem;
|
676 |
+
border-radius: var(--border-radius);
|
677 |
+
}
|
678 |
+
|
679 |
+
.nav-item.active {
|
680 |
+
color: var(--primary-color);
|
681 |
+
}
|
682 |
+
|
683 |
+
.nav-item i {
|
684 |
+
font-size: 1.25rem;
|
685 |
+
}
|
686 |
+
|
687 |
+
.nav-item span {
|
688 |
+
font-size: 0.75rem;
|
689 |
+
font-weight: 500;
|
690 |
+
}
|
691 |
+
|
692 |
+
/* رؤوس الشاشات */
|
693 |
+
.screen-header {
|
694 |
+
background: var(--surface);
|
695 |
+
border-bottom: 1px solid var(--border);
|
696 |
+
padding: 1rem;
|
697 |
+
display: flex;
|
698 |
+
align-items: center;
|
699 |
+
gap: 1rem;
|
700 |
+
position: sticky;
|
701 |
+
top: 0;
|
702 |
+
z-index: 50;
|
703 |
+
}
|
704 |
+
|
705 |
+
.back-btn {
|
706 |
+
background: none;
|
707 |
+
border: none;
|
708 |
+
font-size: 1.25rem;
|
709 |
+
color: var(--text-primary);
|
710 |
+
cursor: pointer;
|
711 |
+
padding: 0.5rem;
|
712 |
+
border-radius: 50%;
|
713 |
+
transition: var(--transition);
|
714 |
+
}
|
715 |
+
|
716 |
+
.back-btn:hover {
|
717 |
+
background: var(--background);
|
718 |
+
}
|
719 |
+
|
720 |
+
.screen-header h2 {
|
721 |
+
font-size: 1.25rem;
|
722 |
+
font-weight: 600;
|
723 |
+
}
|
724 |
+
|
725 |
+
/* النوافذ المنبثقة */
|
726 |
+
.modal-overlay {
|
727 |
+
position: fixed;
|
728 |
+
top: 0;
|
729 |
+
left: 0;
|
730 |
+
width: 100%;
|
731 |
+
height: 100%;
|
732 |
+
background: rgba(0, 0, 0, 0.5);
|
733 |
+
display: none;
|
734 |
+
z-index: 1000;
|
735 |
+
backdrop-filter: blur(3px);
|
736 |
+
}
|
737 |
+
|
738 |
+
.modal-overlay.active {
|
739 |
+
display: flex;
|
740 |
+
align-items: center;
|
741 |
+
justify-content: center;
|
742 |
+
}
|
743 |
+
|
744 |
+
/* شاشة التحويل */
|
745 |
+
.transfer-content {
|
746 |
+
padding: 2rem 1rem 6rem;
|
747 |
+
max-width: 500px;
|
748 |
+
margin: 0 auto;
|
749 |
+
}
|
750 |
+
|
751 |
+
.transfer-steps {
|
752 |
+
margin-bottom: 2rem;
|
753 |
+
}
|
754 |
+
|
755 |
+
.step-indicator {
|
756 |
+
display: flex;
|
757 |
+
align-items: center;
|
758 |
+
justify-content: center;
|
759 |
+
margin-bottom: 1rem;
|
760 |
+
}
|
761 |
+
|
762 |
+
.step-indicator .step {
|
763 |
+
width: 40px;
|
764 |
+
height: 40px;
|
765 |
+
border-radius: 50%;
|
766 |
+
background: var(--border);
|
767 |
+
color: var(--text-secondary);
|
768 |
+
display: flex;
|
769 |
+
align-items: center;
|
770 |
+
justify-content: center;
|
771 |
+
font-weight: 600;
|
772 |
+
transition: var(--transition);
|
773 |
+
}
|
774 |
+
|
775 |
+
.step-indicator .step.active {
|
776 |
+
background: var(--primary-color);
|
777 |
+
color: white;
|
778 |
+
}
|
779 |
+
|
780 |
+
.step-indicator .step.completed {
|
781 |
+
background: var(--success-color);
|
782 |
+
color: white;
|
783 |
+
}
|
784 |
+
|
785 |
+
.step-line {
|
786 |
+
flex: 1;
|
787 |
+
height: 2px;
|
788 |
+
background: var(--border);
|
789 |
+
margin: 0 1rem;
|
790 |
+
}
|
791 |
+
|
792 |
+
.step-labels {
|
793 |
+
display: flex;
|
794 |
+
justify-content: space-between;
|
795 |
+
font-size: 0.875rem;
|
796 |
+
color: var(--text-secondary);
|
797 |
+
}
|
798 |
+
|
799 |
+
.transfer-step-content {
|
800 |
+
display: none;
|
801 |
+
animation: fadeInUp 0.5s ease;
|
802 |
+
}
|
803 |
+
|
804 |
+
.transfer-step-content.active {
|
805 |
+
display: block;
|
806 |
+
}
|
807 |
+
|
808 |
+
.transfer-step-content h3 {
|
809 |
+
font-size: 1.25rem;
|
810 |
+
font-weight: 600;
|
811 |
+
margin-bottom: 1.5rem;
|
812 |
+
text-align: center;
|
813 |
+
}
|
814 |
+
|
815 |
+
/* اختيار المحفظة */
|
816 |
+
.wallet-selection {
|
817 |
+
display: flex;
|
818 |
+
flex-direction: column;
|
819 |
+
gap: 1rem;
|
820 |
+
}
|
821 |
+
|
822 |
+
.wallet-option {
|
823 |
+
background: var(--surface);
|
824 |
+
border: 2px solid var(--border);
|
825 |
+
border-radius: var(--border-radius);
|
826 |
+
padding: 1rem;
|
827 |
+
display: flex;
|
828 |
+
align-items: center;
|
829 |
+
justify-content: space-between;
|
830 |
+
cursor: pointer;
|
831 |
+
transition: var(--transition);
|
832 |
+
}
|
833 |
+
|
834 |
+
.wallet-option:hover {
|
835 |
+
border-color: var(--primary-color);
|
836 |
+
transform: translateY(-2px);
|
837 |
+
box-shadow: var(--shadow);
|
838 |
+
}
|
839 |
+
|
840 |
+
.wallet-option.selected {
|
841 |
+
border-color: var(--primary-color);
|
842 |
+
background: rgba(37, 99, 235, 0.05);
|
843 |
+
}
|
844 |
+
|
845 |
+
.wallet-option-info {
|
846 |
+
display: flex;
|
847 |
+
align-items: center;
|
848 |
+
gap: 1rem;
|
849 |
+
}
|
850 |
+
|
851 |
+
.wallet-option-icon {
|
852 |
+
width: 50px;
|
853 |
+
height: 50px;
|
854 |
+
border-radius: var(--border-radius);
|
855 |
+
overflow: hidden;
|
856 |
+
border: 1px solid var(--border);
|
857 |
+
}
|
858 |
+
|
859 |
+
.wallet-option-icon img {
|
860 |
+
width: 100%;
|
861 |
+
height: 100%;
|
862 |
+
object-fit: cover;
|
863 |
+
}
|
864 |
+
|
865 |
+
.wallet-option-details h4 {
|
866 |
+
font-size: 1.1rem;
|
867 |
+
font-weight: 600;
|
868 |
+
margin-bottom: 0.25rem;
|
869 |
+
}
|
870 |
+
|
871 |
+
.wallet-option-details p {
|
872 |
+
color: var(--text-secondary);
|
873 |
+
font-size: 0.875rem;
|
874 |
+
}
|
875 |
+
|
876 |
+
.wallet-option-balance {
|
877 |
+
text-align: left;
|
878 |
+
}
|
879 |
+
|
880 |
+
.wallet-option-balance .amount {
|
881 |
+
font-size: 1.25rem;
|
882 |
+
font-weight: 700;
|
883 |
+
color: var(--success-color);
|
884 |
+
}
|
885 |
+
|
886 |
+
/* عناصر النموذج */
|
887 |
+
.form-control {
|
888 |
+
width: 100%;
|
889 |
+
padding: 0.75rem 1rem;
|
890 |
+
border: 1px solid var(--border);
|
891 |
+
border-radius: var(--border-radius);
|
892 |
+
font-family: inherit;
|
893 |
+
font-size: 1rem;
|
894 |
+
transition: var(--transition);
|
895 |
+
}
|
896 |
+
|
897 |
+
.form-control:focus {
|
898 |
+
outline: none;
|
899 |
+
border-color: var(--primary-color);
|
900 |
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
901 |
+
}
|
902 |
+
|
903 |
+
/* اقتراحات المبلغ */
|
904 |
+
.amount-suggestions {
|
905 |
+
display: flex;
|
906 |
+
gap: 0.5rem;
|
907 |
+
margin-top: 0.5rem;
|
908 |
+
flex-wrap: wrap;
|
909 |
+
}
|
910 |
+
|
911 |
+
.amount-btn {
|
912 |
+
background: var(--surface);
|
913 |
+
border: 1px solid var(--border);
|
914 |
+
border-radius: 20px;
|
915 |
+
padding: 0.5rem 1rem;
|
916 |
+
font-size: 0.875rem;
|
917 |
+
cursor: pointer;
|
918 |
+
transition: var(--transition);
|
919 |
+
}
|
920 |
+
|
921 |
+
.amount-btn:hover {
|
922 |
+
background: var(--primary-color);
|
923 |
+
color: white;
|
924 |
+
border-color: var(--primary-color);
|
925 |
+
}
|
926 |
+
|
927 |
+
/* ملخص التحويل */
|
928 |
+
.transfer-summary {
|
929 |
+
background: var(--background);
|
930 |
+
border-radius: var(--border-radius);
|
931 |
+
padding: 1.5rem;
|
932 |
+
margin-bottom: 1.5rem;
|
933 |
+
}
|
934 |
+
|
935 |
+
.summary-item {
|
936 |
+
display: flex;
|
937 |
+
justify-content: space-between;
|
938 |
+
align-items: center;
|
939 |
+
padding: 0.75rem 0;
|
940 |
+
border-bottom: 1px solid var(--border);
|
941 |
+
}
|
942 |
+
|
943 |
+
.summary-item:last-child {
|
944 |
+
border-bottom: none;
|
945 |
+
}
|
946 |
+
|
947 |
+
.summary-item.total {
|
948 |
+
font-weight: 600;
|
949 |
+
font-size: 1.1rem;
|
950 |
+
color: var(--primary-color);
|
951 |
+
border-top: 2px solid var(--border);
|
952 |
+
margin-top: 0.5rem;
|
953 |
+
padding-top: 1rem;
|
954 |
+
}
|
955 |
+
|
956 |
+
/* أزرار التنقل */
|
957 |
+
.transfer-navigation {
|
958 |
+
display: flex;
|
959 |
+
gap: 1rem;
|
960 |
+
margin-top: 2rem;
|
961 |
+
}
|
962 |
+
|
963 |
+
.transfer-navigation button {
|
964 |
+
flex: 1;
|
965 |
+
}
|
966 |
+
|
967 |
+
.hidden {
|
968 |
+
display: none !important;
|
969 |
+
}
|
970 |
+
|
971 |
+
/* نظام الإشعارات والتنبيهات */
|
972 |
+
.toast-container {
|
973 |
+
position: fixed;
|
974 |
+
top: 20px;
|
975 |
+
left: 20px;
|
976 |
+
z-index: 9999;
|
977 |
+
display: flex;
|
978 |
+
flex-direction: column;
|
979 |
+
gap: 10px;
|
980 |
+
max-width: 400px;
|
981 |
+
width: 100%;
|
982 |
+
}
|
983 |
+
|
984 |
+
.toast {
|
985 |
+
background: var(--surface);
|
986 |
+
border-radius: var(--border-radius);
|
987 |
+
box-shadow: var(--shadow-lg);
|
988 |
+
display: flex;
|
989 |
+
align-items: center;
|
990 |
+
gap: 1rem;
|
991 |
+
padding: 1rem;
|
992 |
+
transform: translateX(-100%);
|
993 |
+
transition: var(--transition);
|
994 |
+
border-right: 4px solid var(--primary-color);
|
995 |
+
}
|
996 |
+
|
997 |
+
.toast.toast-show {
|
998 |
+
transform: translateX(0);
|
999 |
+
}
|
1000 |
+
|
1001 |
+
.toast.toast-hide {
|
1002 |
+
transform: translateX(-100%);
|
1003 |
+
opacity: 0;
|
1004 |
+
}
|
1005 |
+
|
1006 |
+
.toast.toast-success {
|
1007 |
+
border-right-color: var(--success-color);
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
.toast.toast-error {
|
1011 |
+
border-right-color: var(--error-color);
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
.toast.toast-warning {
|
1015 |
+
border-right-color: var(--warning-color);
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
.toast-icon {
|
1019 |
+
font-size: 1.5rem;
|
1020 |
+
color: var(--primary-color);
|
1021 |
+
}
|
1022 |
+
|
1023 |
+
.toast.toast-success .toast-icon {
|
1024 |
+
color: var(--success-color);
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
.toast.toast-error .toast-icon {
|
1028 |
+
color: var(--error-color);
|
1029 |
+
}
|
1030 |
+
|
1031 |
+
.toast.toast-warning .toast-icon {
|
1032 |
+
color: var(--warning-color);
|
1033 |
+
}
|
1034 |
+
|
1035 |
+
.toast-content {
|
1036 |
+
flex: 1;
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
.toast-message {
|
1040 |
+
font-weight: 500;
|
1041 |
+
line-height: 1.4;
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
.toast-close {
|
1045 |
+
background: none;
|
1046 |
+
border: none;
|
1047 |
+
color: var(--text-secondary);
|
1048 |
+
cursor: pointer;
|
1049 |
+
padding: 0.25rem;
|
1050 |
+
border-radius: 50%;
|
1051 |
+
transition: var(--transition);
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
.toast-close:hover {
|
1055 |
+
background: var(--background);
|
1056 |
+
color: var(--text-primary);
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
/* المودال */
|
1060 |
+
.modal-container {
|
1061 |
+
position: fixed;
|
1062 |
+
top: 0;
|
1063 |
+
left: 0;
|
1064 |
+
width: 100%;
|
1065 |
+
height: 100%;
|
1066 |
+
z-index: 9998;
|
1067 |
+
display: none;
|
1068 |
+
align-items: center;
|
1069 |
+
justify-content: center;
|
1070 |
+
padding: 1rem;
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
.modal-overlay {
|
1074 |
+
position: absolute;
|
1075 |
+
top: 0;
|
1076 |
+
left: 0;
|
1077 |
+
width: 100%;
|
1078 |
+
height: 100%;
|
1079 |
+
background: rgba(0, 0, 0, 0.5);
|
1080 |
+
backdrop-filter: blur(3px);
|
1081 |
+
-webkit-backdrop-filter: blur(3px);
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
.modal-content {
|
1085 |
+
background: var(--surface);
|
1086 |
+
border-radius: var(--border-radius);
|
1087 |
+
box-shadow: var(--shadow-lg);
|
1088 |
+
max-width: 500px;
|
1089 |
+
width: 100%;
|
1090 |
+
max-height: 80vh;
|
1091 |
+
overflow-y: auto;
|
1092 |
+
position: relative;
|
1093 |
+
z-index: 1;
|
1094 |
+
animation: modalSlideIn 0.3s ease;
|
1095 |
+
}
|
1096 |
+
|
1097 |
+
@keyframes modalSlideIn {
|
1098 |
+
from {
|
1099 |
+
opacity: 0;
|
1100 |
+
transform: scale(0.9) translateY(-20px);
|
1101 |
+
}
|
1102 |
+
to {
|
1103 |
+
opacity: 1;
|
1104 |
+
transform: scale(1) translateY(0);
|
1105 |
+
}
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
.modal-header {
|
1109 |
+
display: flex;
|
1110 |
+
justify-content: space-between;
|
1111 |
+
align-items: center;
|
1112 |
+
padding: 1.5rem;
|
1113 |
+
border-bottom: 1px solid var(--border);
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
.modal-title {
|
1117 |
+
font-size: 1.25rem;
|
1118 |
+
font-weight: 600;
|
1119 |
+
margin: 0;
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
.modal-close {
|
1123 |
+
background: none;
|
1124 |
+
border: none;
|
1125 |
+
font-size: 1.25rem;
|
1126 |
+
color: var(--text-secondary);
|
1127 |
+
cursor: pointer;
|
1128 |
+
padding: 0.5rem;
|
1129 |
+
border-radius: 50%;
|
1130 |
+
transition: var(--transition);
|
1131 |
+
}
|
1132 |
+
|
1133 |
+
.modal-close:hover {
|
1134 |
+
background: var(--background);
|
1135 |
+
color: var(--text-primary);
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
.modal-body {
|
1139 |
+
padding: 1.5rem;
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
.modal-footer {
|
1143 |
+
padding: 1rem 1.5rem;
|
1144 |
+
border-top: 1px solid var(--border);
|
1145 |
+
display: flex;
|
1146 |
+
gap: 1rem;
|
1147 |
+
justify-content: flex-end;
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
.modal-footer .btn {
|
1151 |
+
min-width: 100px;
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
/* مودال التحميل */
|
1155 |
+
.loading-modal .modal-content {
|
1156 |
+
max-width: 300px;
|
1157 |
+
text-align: center;
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
.loading-content {
|
1161 |
+
padding: 2rem;
|
1162 |
+
}
|
1163 |
+
|
1164 |
+
.loading-content .loading-spinner {
|
1165 |
+
width: 50px;
|
1166 |
+
height: 50px;
|
1167 |
+
border: 4px solid var(--border);
|
1168 |
+
border-top: 4px solid var(--primary-color);
|
1169 |
+
border-radius: 50%;
|
1170 |
+
animation: spin 1s linear infinite;
|
1171 |
+
margin: 0 auto 1rem;
|
1172 |
+
}
|
1173 |
+
|
1174 |
+
.loading-content p {
|
1175 |
+
color: var(--text-secondary);
|
1176 |
+
margin: 0;
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
/* قائمة الإشعارات */
|
1180 |
+
.notifications-header {
|
1181 |
+
display: flex;
|
1182 |
+
justify-content: space-between;
|
1183 |
+
align-items: center;
|
1184 |
+
margin-bottom: 1rem;
|
1185 |
+
}
|
1186 |
+
|
1187 |
+
.notifications-list {
|
1188 |
+
max-height: 400px;
|
1189 |
+
overflow-y: auto;
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
.notification-item {
|
1193 |
+
display: flex;
|
1194 |
+
align-items: flex-start;
|
1195 |
+
gap: 1rem;
|
1196 |
+
padding: 1rem;
|
1197 |
+
border-bottom: 1px solid var(--border);
|
1198 |
+
transition: var(--transition);
|
1199 |
+
}
|
1200 |
+
|
1201 |
+
.notification-item:hover {
|
1202 |
+
background: var(--background);
|
1203 |
+
}
|
1204 |
+
|
1205 |
+
.notification-item.unread {
|
1206 |
+
background: rgba(37, 99, 235, 0.05);
|
1207 |
+
border-right: 3px solid var(--primary-color);
|
1208 |
+
}
|
1209 |
+
|
1210 |
+
.notification-icon {
|
1211 |
+
width: 40px;
|
1212 |
+
height: 40px;
|
1213 |
+
border-radius: 50%;
|
1214 |
+
display: flex;
|
1215 |
+
align-items: center;
|
1216 |
+
justify-content: center;
|
1217 |
+
font-size: 1rem;
|
1218 |
+
flex-shrink: 0;
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
.notification-icon.transaction {
|
1222 |
+
background: rgba(37, 99, 235, 0.1);
|
1223 |
+
color: var(--primary-color);
|
1224 |
+
}
|
1225 |
+
|
1226 |
+
.notification-icon.security {
|
1227 |
+
background: rgba(220, 38, 38, 0.1);
|
1228 |
+
color: var(--error-color);
|
1229 |
+
}
|
1230 |
+
|
1231 |
+
.notification-icon.system {
|
1232 |
+
background: rgba(107, 114, 128, 0.1);
|
1233 |
+
color: var(--text-secondary);
|
1234 |
+
}
|
1235 |
+
|
1236 |
+
.notification-content {
|
1237 |
+
flex: 1;
|
1238 |
+
}
|
1239 |
+
|
1240 |
+
.notification-content h4 {
|
1241 |
+
font-size: 1rem;
|
1242 |
+
font-weight: 600;
|
1243 |
+
margin: 0 0 0.25rem 0;
|
1244 |
+
}
|
1245 |
+
|
1246 |
+
.notification-content p {
|
1247 |
+
color: var(--text-secondary);
|
1248 |
+
margin: 0 0 0.5rem 0;
|
1249 |
+
line-height: 1.4;
|
1250 |
+
}
|
1251 |
+
|
1252 |
+
.notification-time {
|
1253 |
+
font-size: 0.875rem;
|
1254 |
+
color: var(--text-secondary);
|
1255 |
+
}
|
1256 |
+
|
1257 |
+
.notification-actions {
|
1258 |
+
display: flex;
|
1259 |
+
gap: 0.5rem;
|
1260 |
+
flex-shrink: 0;
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
.notification-actions button {
|
1264 |
+
background: none;
|
1265 |
+
border: none;
|
1266 |
+
color: var(--text-secondary);
|
1267 |
+
cursor: pointer;
|
1268 |
+
padding: 0.5rem;
|
1269 |
+
border-radius: 50%;
|
1270 |
+
transition: var(--transition);
|
1271 |
+
}
|
1272 |
+
|
1273 |
+
.notification-actions button:hover {
|
1274 |
+
background: var(--background);
|
1275 |
+
color: var(--text-primary);
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
.no-notifications {
|
1279 |
+
text-align: center;
|
1280 |
+
color: var(--text-secondary);
|
1281 |
+
padding: 2rem;
|
1282 |
+
font-style: italic;
|
1283 |
+
}
|
1284 |
+
|
1285 |
+
/* الاستجابة للشاشات الصغيرة */
|
1286 |
+
@media (max-width: 768px) {
|
1287 |
+
.login-container {
|
1288 |
+
padding: 1rem;
|
1289 |
+
}
|
1290 |
+
|
1291 |
+
.wallet-logos {
|
1292 |
+
grid-template-columns: repeat(2, 1fr);
|
1293 |
+
}
|
1294 |
+
|
1295 |
+
.actions-grid {
|
1296 |
+
grid-template-columns: repeat(2, 1fr);
|
1297 |
+
}
|
1298 |
+
|
1299 |
+
.main-header {
|
1300 |
+
padding: 1.5rem 1rem;
|
1301 |
+
}
|
1302 |
+
|
1303 |
+
.balance-amount {
|
1304 |
+
font-size: 2rem;
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
.wallet-card {
|
1308 |
+
padding: 1rem;
|
1309 |
+
}
|
1310 |
+
|
1311 |
+
.transaction-item {
|
1312 |
+
padding: 0.75rem;
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
.transfer-content {
|
1316 |
+
padding: 1rem 1rem 6rem;
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
.step-labels {
|
1320 |
+
font-size: 0.75rem;
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
.wallet-option {
|
1324 |
+
padding: 0.75rem;
|
1325 |
+
}
|
1326 |
+
|
1327 |
+
.amount-suggestions {
|
1328 |
+
justify-content: center;
|
1329 |
+
}
|
1330 |
+
|
1331 |
+
.transfer-navigation {
|
1332 |
+
flex-direction: column;
|
1333 |
+
}
|
1334 |
+
}
|
tsconfig.app.json
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"extends": "./tsconfig.json",
|
3 |
+
"compilerOptions": {
|
4 |
+
"outDir": "./out-tsc/app",
|
5 |
+
"types": []
|
6 |
+
},
|
7 |
+
"files": [
|
8 |
+
"src/main.ts"
|
9 |
+
],
|
10 |
+
"include": [
|
11 |
+
"src/**/*.d.ts"
|
12 |
+
]
|
13 |
+
}
|
tsconfig.json
CHANGED
@@ -1,27 +1,32 @@
|
|
1 |
{
|
|
|
2 |
"compilerOptions": {
|
3 |
-
"
|
4 |
-
"
|
5 |
-
"
|
6 |
-
"skipLibCheck": true,
|
7 |
"strict": true,
|
8 |
-
"
|
9 |
-
"
|
10 |
-
"
|
11 |
-
"
|
12 |
-
"
|
13 |
-
"
|
14 |
-
"
|
15 |
-
"
|
16 |
-
"
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
"
|
22 |
-
"
|
23 |
-
|
|
|
24 |
},
|
25 |
-
"
|
26 |
-
|
|
|
|
|
|
|
|
|
27 |
}
|
|
|
1 |
{
|
2 |
+
"compileOnSave": false,
|
3 |
"compilerOptions": {
|
4 |
+
"baseUrl": "./",
|
5 |
+
"outDir": "./dist/out-tsc",
|
6 |
+
"forceConsistentCasingInFileNames": true,
|
|
|
7 |
"strict": true,
|
8 |
+
"noImplicitOverride": true,
|
9 |
+
"noPropertyAccessFromIndexSignature": true,
|
10 |
+
"noImplicitReturns": true,
|
11 |
+
"noFallthroughCasesInSwitch": true,
|
12 |
+
"sourceMap": true,
|
13 |
+
"declaration": false,
|
14 |
+
"downlevelIteration": true,
|
15 |
+
"experimentalDecorators": true,
|
16 |
+
"moduleResolution": "node",
|
17 |
+
"importHelpers": true,
|
18 |
+
"target": "ES2022",
|
19 |
+
"module": "ES2022",
|
20 |
+
"useDefineForClassFields": false,
|
21 |
+
"lib": [
|
22 |
+
"ES2022",
|
23 |
+
"dom"
|
24 |
+
]
|
25 |
},
|
26 |
+
"angularCompilerOptions": {
|
27 |
+
"enableI18nLegacyMessageIdFormat": false,
|
28 |
+
"strictInjectionParameters": true,
|
29 |
+
"strictInputAccessModifiers": true,
|
30 |
+
"strictTemplates": true
|
31 |
+
}
|
32 |
}
|
wallets.js
ADDED
@@ -0,0 +1,430 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// إدارة المحافظ الإلكترونية
|
2 |
+
class WalletManager {
|
3 |
+
constructor() {
|
4 |
+
this.supportedWallets = {
|
5 |
+
jawali: {
|
6 |
+
id: 'jawali',
|
7 |
+
name: 'جوالي',
|
8 |
+
nameEn: 'Jawali',
|
9 |
+
provider: 'WeCash YE',
|
10 |
+
packageId: 'com.ama.wecashmobileapp',
|
11 |
+
icon: 'https://play-lh.googleusercontent.com/NuKV_Snecpx633RHzIWvFauG32WBAk-jt3lcZOajw2w7VD8Hdt8h5Lb0pYLopB_Qk4Y=w240-h480',
|
12 |
+
color: '#4361ee',
|
13 |
+
features: ['transfer', 'bills', 'recharge', 'qr'],
|
14 |
+
supportedCurrencies: ['YER', 'SAR', 'USD'],
|
15 |
+
maxTransfer: 3000000,
|
16 |
+
fees: {
|
17 |
+
transfer: 20,
|
18 |
+
billPayment: 15,
|
19 |
+
recharge: 10
|
20 |
+
}
|
21 |
+
},
|
22 |
+
onecash: {
|
23 |
+
id: 'onecash',
|
24 |
+
name: 'ONE Cash',
|
25 |
+
nameEn: 'ONE Cash',
|
26 |
+
provider: 'ONECASHYE',
|
27 |
+
packageId: 'com.one.onecustomer',
|
28 |
+
icon: 'https://play-lh.googleusercontent.com/OF2xMRUmNH47BIaOTbv7GgvmLRTQbT1xHkFF_Ocswx6Jq5gfX4VlfAl_275UmjH2pg=w240-h480',
|
29 |
+
color: '#059669',
|
30 |
+
features: ['transfer', 'bills', 'recharge', 'qr', 'international'],
|
31 |
+
supportedCurrencies: ['YER', 'SAR', 'USD'],
|
32 |
+
maxTransfer: 3000000,
|
33 |
+
fees: {
|
34 |
+
transfer: 0, // مجاني للمستخدمين
|
35 |
+
billPayment: 10,
|
36 |
+
recharge: 5
|
37 |
+
}
|
38 |
+
},
|
39 |
+
cash: {
|
40 |
+
id: 'cash',
|
41 |
+
name: 'Cash',
|
42 |
+
nameEn: 'Cash',
|
43 |
+
provider: 'Tamkeen Financial',
|
44 |
+
packageId: 'com.tamkeen.sms',
|
45 |
+
icon: 'https://play-lh.googleusercontent.com/zkV8HeO6iF2xa77ObHdKfAXfrfjU5fgWB0XsWt7_DmG4VGSKob2jU_CrqWyKQtghQyE=w240-h480',
|
46 |
+
color: '#dc2626',
|
47 |
+
features: ['transfer', 'bills', 'recharge', 'qr', 'offline'],
|
48 |
+
supportedCurrencies: ['YER'],
|
49 |
+
maxTransfer: 2000000,
|
50 |
+
fees: {
|
51 |
+
transfer: 25,
|
52 |
+
billPayment: 20,
|
53 |
+
recharge: 15
|
54 |
+
}
|
55 |
+
},
|
56 |
+
jaib: {
|
57 |
+
id: 'jaib',
|
58 |
+
name: 'Jaib',
|
59 |
+
nameEn: 'Jaib Digital Wallet',
|
60 |
+
provider: 'AHD Financial',
|
61 |
+
packageId: 'com.ahd.jaib',
|
62 |
+
icon: 'https://play-lh.googleusercontent.com/EAaXnjh1FVPYpI5qWZwWvZIV5oD1hm9auDX0owOgREBjMAzYKX9od1USWRzlXIhRvKMx=w240-h480',
|
63 |
+
color: '#7c3aed',
|
64 |
+
features: ['transfer', 'bills', 'recharge', 'qr', 'entertainment'],
|
65 |
+
supportedCurrencies: ['YER'],
|
66 |
+
maxTransfer: 1500000,
|
67 |
+
fees: {
|
68 |
+
transfer: 15,
|
69 |
+
billPayment: 12,
|
70 |
+
recharge: 8
|
71 |
+
}
|
72 |
+
},
|
73 |
+
mfloos: {
|
74 |
+
id: 'mfloos',
|
75 |
+
name: 'mFloos',
|
76 |
+
nameEn: 'mFloos - Customers',
|
77 |
+
provider: 'Alkuraimi Islamic Microfinance Bank',
|
78 |
+
packageId: 'wallet.mfloos.com.mflooswallet.customer',
|
79 |
+
icon: 'https://play-lh.googleusercontent.com/mNvakjEk-3VX7icU5w4xmAhT4MQgGAGcQYpRvPkBLVTzD-sYnmzAH_wuglMujTsaqQ=w240-h480',
|
80 |
+
color: '#0891b2',
|
81 |
+
features: ['transfer', 'bills', 'recharge', 'banking'],
|
82 |
+
supportedCurrencies: ['YER'],
|
83 |
+
maxTransfer: 1000000,
|
84 |
+
fees: {
|
85 |
+
transfer: 10,
|
86 |
+
billPayment: 8,
|
87 |
+
recharge: 5
|
88 |
+
}
|
89 |
+
},
|
90 |
+
mobilemoney: {
|
91 |
+
id: 'mobilemoney',
|
92 |
+
name: 'Mobile Money',
|
93 |
+
nameEn: 'Mobile Money Wallet',
|
94 |
+
provider: 'CAC Bank',
|
95 |
+
packageId: 'cac.mobilemoney.app',
|
96 |
+
icon: 'https://play-lh.googleusercontent.com/51r7PLMlK0gVvITgOoJ7BnGX-9Gq3_ayiSiHHxSDbJZPCgABXI_LnU6jNCHAefWHvSPV=w240-h480',
|
97 |
+
color: '#ea580c',
|
98 |
+
features: ['transfer', 'bills', 'recharge', 'atm'],
|
99 |
+
supportedCurrencies: ['YER'],
|
100 |
+
maxTransfer: 2500000,
|
101 |
+
fees: {
|
102 |
+
transfer: 18,
|
103 |
+
billPayment: 15,
|
104 |
+
recharge: 12
|
105 |
+
}
|
106 |
+
}
|
107 |
+
};
|
108 |
+
|
109 |
+
this.userWallets = [];
|
110 |
+
this.init();
|
111 |
+
}
|
112 |
+
|
113 |
+
// تهيئة مدير المحافظ
|
114 |
+
init() {
|
115 |
+
this.loadUserWallets();
|
116 |
+
}
|
117 |
+
|
118 |
+
// تحميل محافظ المستخدم
|
119 |
+
loadUserWallets() {
|
120 |
+
const savedWallets = localStorage.getItem('unifiedWallet_userWallets');
|
121 |
+
if (savedWallets) {
|
122 |
+
this.userWallets = JSON.parse(savedWallets);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
// حفظ محافظ المستخدم
|
127 |
+
saveUserWallets() {
|
128 |
+
localStorage.setItem('unifiedWallet_userWallets', JSON.stringify(this.userWallets));
|
129 |
+
}
|
130 |
+
|
131 |
+
// إضافة محفظة جديدة
|
132 |
+
async addWallet(walletId, accountNumber, pin) {
|
133 |
+
const walletInfo = this.supportedWallets[walletId];
|
134 |
+
if (!walletInfo) {
|
135 |
+
throw new Error('محفظة غير مدعومة');
|
136 |
+
}
|
137 |
+
|
138 |
+
// التحقق من صحة البيانات
|
139 |
+
if (!this.validateAccountNumber(accountNumber)) {
|
140 |
+
throw new Error('رقم الحساب غير صحيح');
|
141 |
+
}
|
142 |
+
|
143 |
+
if (!this.validatePin(pin)) {
|
144 |
+
throw new Error('رمز PIN غير صحيح');
|
145 |
+
}
|
146 |
+
|
147 |
+
// التحقق من عدم وجود المحفظة مسبقاً
|
148 |
+
const existingWallet = this.userWallets.find(w => w.id === walletId);
|
149 |
+
if (existingWallet) {
|
150 |
+
throw new Error('هذه المحفظة مضافة مسبقاً');
|
151 |
+
}
|
152 |
+
|
153 |
+
// محاكاة التحقق من المحفظة
|
154 |
+
await this.verifyWalletCredentials(walletId, accountNumber, pin);
|
155 |
+
|
156 |
+
// إضافة المحفظة
|
157 |
+
const newWallet = {
|
158 |
+
id: walletId,
|
159 |
+
accountNumber: accountNumber,
|
160 |
+
pin: this.encryptPin(pin),
|
161 |
+
balance: await this.fetchWalletBalance(walletId, accountNumber),
|
162 |
+
status: 'active',
|
163 |
+
addedAt: new Date().toISOString(),
|
164 |
+
lastSync: new Date().toISOString()
|
165 |
+
};
|
166 |
+
|
167 |
+
this.userWallets.push(newWallet);
|
168 |
+
this.saveUserWallets();
|
169 |
+
|
170 |
+
return newWallet;
|
171 |
+
}
|
172 |
+
|
173 |
+
// إزالة محفظة
|
174 |
+
removeWallet(walletId) {
|
175 |
+
const index = this.userWallets.findIndex(w => w.id === walletId);
|
176 |
+
if (index === -1) {
|
177 |
+
throw new Error('المحفظة غير موجودة');
|
178 |
+
}
|
179 |
+
|
180 |
+
this.userWallets.splice(index, 1);
|
181 |
+
this.saveUserWallets();
|
182 |
+
}
|
183 |
+
|
184 |
+
// تحديث رصيد محفظة
|
185 |
+
async updateWalletBalance(walletId) {
|
186 |
+
const wallet = this.userWallets.find(w => w.id === walletId);
|
187 |
+
if (!wallet) {
|
188 |
+
throw new Error('المحفظة غير موجودة');
|
189 |
+
}
|
190 |
+
|
191 |
+
const newBalance = await this.fetchWalletBalance(walletId, wallet.accountNumber);
|
192 |
+
wallet.balance = newBalance;
|
193 |
+
wallet.lastSync = new Date().toISOString();
|
194 |
+
|
195 |
+
this.saveUserWallets();
|
196 |
+
return newBalance;
|
197 |
+
}
|
198 |
+
|
199 |
+
// تحديث جميع الأرصدة
|
200 |
+
async updateAllBalances() {
|
201 |
+
const updatePromises = this.userWallets.map(wallet =>
|
202 |
+
this.updateWalletBalance(wallet.id)
|
203 |
+
);
|
204 |
+
|
205 |
+
await Promise.all(updatePromises);
|
206 |
+
}
|
207 |
+
|
208 |
+
// تنفيذ تحويل
|
209 |
+
async executeTransfer(fromWalletId, toWalletId, amount, recipientNumber, pin) {
|
210 |
+
const fromWallet = this.userWallets.find(w => w.id === fromWalletId);
|
211 |
+
if (!fromWallet) {
|
212 |
+
throw new Error('محفظة المرسل غير موجودة');
|
213 |
+
}
|
214 |
+
|
215 |
+
const fromWalletInfo = this.supportedWallets[fromWalletId];
|
216 |
+
const toWalletInfo = this.supportedWallets[toWalletId];
|
217 |
+
|
218 |
+
// التحقق من الرصيد
|
219 |
+
const fee = fromWalletInfo.fees.transfer;
|
220 |
+
const totalAmount = amount + fee;
|
221 |
+
|
222 |
+
if (fromWallet.balance < totalAmount) {
|
223 |
+
throw new Error('الرصيد غير كافي');
|
224 |
+
}
|
225 |
+
|
226 |
+
// التحقق من الحد الأقصى للتحويل
|
227 |
+
if (amount > fromWalletInfo.maxTransfer) {
|
228 |
+
throw new Error(`الحد الأقصى للتحويل هو ${fromWalletInfo.maxTransfer.toLocaleString()} ر.ي`);
|
229 |
+
}
|
230 |
+
|
231 |
+
// التحقق من رمز PIN
|
232 |
+
if (!this.verifyPin(pin, fromWallet.pin)) {
|
233 |
+
throw new Error('رمز PIN غير صحيح');
|
234 |
+
}
|
235 |
+
|
236 |
+
// تنفيذ التحويل
|
237 |
+
await this.processTransfer({
|
238 |
+
fromWallet: fromWalletId,
|
239 |
+
toWallet: toWalletId,
|
240 |
+
amount: amount,
|
241 |
+
fee: fee,
|
242 |
+
recipientNumber: recipientNumber,
|
243 |
+
timestamp: new Date().toISOString()
|
244 |
+
});
|
245 |
+
|
246 |
+
// تحديث الرصيد
|
247 |
+
fromWallet.balance -= totalAmount;
|
248 |
+
this.saveUserWallets();
|
249 |
+
|
250 |
+
return {
|
251 |
+
success: true,
|
252 |
+
transactionId: this.generateTransactionId(),
|
253 |
+
amount: amount,
|
254 |
+
fee: fee,
|
255 |
+
total: totalAmount,
|
256 |
+
newBalance: fromWallet.balance
|
257 |
+
};
|
258 |
+
}
|
259 |
+
|
260 |
+
// دفع فاتورة
|
261 |
+
async payBill(walletId, billType, billNumber, amount, pin) {
|
262 |
+
const wallet = this.userWallets.find(w => w.id === walletId);
|
263 |
+
if (!wallet) {
|
264 |
+
throw new Error('المحفظة غير موجودة');
|
265 |
+
}
|
266 |
+
|
267 |
+
const walletInfo = this.supportedWallets[walletId];
|
268 |
+
const fee = walletInfo.fees.billPayment;
|
269 |
+
const totalAmount = amount + fee;
|
270 |
+
|
271 |
+
if (wallet.balance < totalAmount) {
|
272 |
+
throw new Error('الرصيد غير كافي');
|
273 |
+
}
|
274 |
+
|
275 |
+
if (!this.verifyPin(pin, wallet.pin)) {
|
276 |
+
throw new Error('رمز PIN غير صحيح');
|
277 |
+
}
|
278 |
+
|
279 |
+
// تنفيذ دفع الفاتورة
|
280 |
+
await this.processBillPayment({
|
281 |
+
wallet: walletId,
|
282 |
+
billType: billType,
|
283 |
+
billNumber: billNumber,
|
284 |
+
amount: amount,
|
285 |
+
fee: fee,
|
286 |
+
timestamp: new Date().toISOString()
|
287 |
+
});
|
288 |
+
|
289 |
+
wallet.balance -= totalAmount;
|
290 |
+
this.saveUserWallets();
|
291 |
+
|
292 |
+
return {
|
293 |
+
success: true,
|
294 |
+
transactionId: this.generateTransactionId(),
|
295 |
+
amount: amount,
|
296 |
+
fee: fee,
|
297 |
+
total: totalAmount,
|
298 |
+
newBalance: wallet.balance
|
299 |
+
};
|
300 |
+
}
|
301 |
+
|
302 |
+
// شحن رصيد
|
303 |
+
async rechargeBalance(walletId, phoneNumber, amount, pin) {
|
304 |
+
const wallet = this.userWallets.find(w => w.id === walletId);
|
305 |
+
if (!wallet) {
|
306 |
+
throw new Error('المحفظة غير موجودة');
|
307 |
+
}
|
308 |
+
|
309 |
+
const walletInfo = this.supportedWallets[walletId];
|
310 |
+
const fee = walletInfo.fees.recharge;
|
311 |
+
const totalAmount = amount + fee;
|
312 |
+
|
313 |
+
if (wallet.balance < totalAmount) {
|
314 |
+
throw new Error('الرصيد غير كافي');
|
315 |
+
}
|
316 |
+
|
317 |
+
if (!this.verifyPin(pin, wallet.pin)) {
|
318 |
+
throw new Error('رمز PIN غير صحيح');
|
319 |
+
}
|
320 |
+
|
321 |
+
// تنفيذ شحن الرصيد
|
322 |
+
await this.processRecharge({
|
323 |
+
wallet: walletId,
|
324 |
+
phoneNumber: phoneNumber,
|
325 |
+
amount: amount,
|
326 |
+
fee: fee,
|
327 |
+
timestamp: new Date().toISOString()
|
328 |
+
});
|
329 |
+
|
330 |
+
wallet.balance -= totalAmount;
|
331 |
+
this.saveUserWallets();
|
332 |
+
|
333 |
+
return {
|
334 |
+
success: true,
|
335 |
+
transactionId: this.generateTransactionId(),
|
336 |
+
amount: amount,
|
337 |
+
fee: fee,
|
338 |
+
total: totalAmount,
|
339 |
+
newBalance: wallet.balance
|
340 |
+
};
|
341 |
+
}
|
342 |
+
|
343 |
+
// الحصول على معلومات محفظة
|
344 |
+
getWalletInfo(walletId) {
|
345 |
+
return this.supportedWallets[walletId];
|
346 |
+
}
|
347 |
+
|
348 |
+
// الحصول على محافظ المستخدم
|
349 |
+
getUserWallets() {
|
350 |
+
return this.userWallets.map(userWallet => {
|
351 |
+
const walletInfo = this.supportedWallets[userWallet.id];
|
352 |
+
return {
|
353 |
+
...walletInfo,
|
354 |
+
...userWallet,
|
355 |
+
accountNumber: userWallet.accountNumber
|
356 |
+
};
|
357 |
+
});
|
358 |
+
}
|
359 |
+
|
360 |
+
// التحقق من صحة رقم الحساب
|
361 |
+
validateAccountNumber(accountNumber) {
|
362 |
+
return /^[0-9]{9}$/.test(accountNumber);
|
363 |
+
}
|
364 |
+
|
365 |
+
// التحقق من صحة رمز PIN
|
366 |
+
validatePin(pin) {
|
367 |
+
return /^[0-9]{4,6}$/.test(pin);
|
368 |
+
}
|
369 |
+
|
370 |
+
// تشفير رمز PIN
|
371 |
+
encryptPin(pin) {
|
372 |
+
// تشفير بسيط للتجربة - في الإنتاج يجب استخدام تشفير قوي
|
373 |
+
return btoa(pin);
|
374 |
+
}
|
375 |
+
|
376 |
+
// فك تشفير رمز PIN
|
377 |
+
decryptPin(encryptedPin) {
|
378 |
+
return atob(encryptedPin);
|
379 |
+
}
|
380 |
+
|
381 |
+
// التحقق من رمز PIN
|
382 |
+
verifyPin(pin, encryptedPin) {
|
383 |
+
return pin === this.decryptPin(encryptedPin);
|
384 |
+
}
|
385 |
+
|
386 |
+
// توليد معرف معاملة
|
387 |
+
generateTransactionId() {
|
388 |
+
return 'TXN' + Date.now() + Math.random().toString(36).substr(2, 5).toUpperCase();
|
389 |
+
}
|
390 |
+
|
391 |
+
// محاكاة التحقق من بيانات المحفظة
|
392 |
+
async verifyWalletCredentials(walletId, accountNumber, pin) {
|
393 |
+
// محاكاة استدعاء API للتحقق
|
394 |
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
395 |
+
|
396 |
+
// في التطبيق الحقيقي، سيتم التحقق من البيانات مع خوادم المحفظة
|
397 |
+
return true;
|
398 |
+
}
|
399 |
+
|
400 |
+
// محاكاة جلب رصيد المحفظة
|
401 |
+
async fetchWalletBalance(walletId, accountNumber) {
|
402 |
+
// محاكاة استدعاء API لجلب الرصيد
|
403 |
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
404 |
+
|
405 |
+
// إرجاع رصيد عشوائي للتجربة
|
406 |
+
return Math.floor(Math.random() * 50000) + 1000;
|
407 |
+
}
|
408 |
+
|
409 |
+
// محاكاة تنفيذ التحويل
|
410 |
+
async processTransfer(transferData) {
|
411 |
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
412 |
+
// في التطبيق الحقيقي، سيتم إرسال البيانات لخوادم المحفظة
|
413 |
+
console.log('تم تنفيذ التحويل:', transferData);
|
414 |
+
}
|
415 |
+
|
416 |
+
// محاكاة دفع الفاتورة
|
417 |
+
async processBillPayment(billData) {
|
418 |
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
419 |
+
console.log('تم دفع الفاتورة:', billData);
|
420 |
+
}
|
421 |
+
|
422 |
+
// محاكاة شحن الرصيد
|
423 |
+
async processRecharge(rechargeData) {
|
424 |
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
425 |
+
console.log('تم شحن الرصيد:', rechargeData);
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
// تصدير الكلاس للاستخدام في التطبيق الرئيسي
|
430 |
+
window.WalletManager = WalletManager;
|
الاخير.html
ADDED
@@ -0,0 +1,618 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="ar" dir="rtl">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>نظام الدفع متعدد المحافظ</title>
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap" rel="stylesheet">
|
8 |
+
<style>
|
9 |
+
:root {
|
10 |
+
--primary: #4361ee;
|
11 |
+
--secondary: #3f37c9;
|
12 |
+
--success: #2e7d32;
|
13 |
+
--warning: #e63946;
|
14 |
+
--light: #f8f9fa;
|
15 |
+
--dark: #212529;
|
16 |
+
--gray: #6c757d;
|
17 |
+
--border: #dee2e6;
|
18 |
+
--shadow: 0 4px 6px rgba(0,0,0,0.1);
|
19 |
+
}
|
20 |
+
|
21 |
+
body {
|
22 |
+
font-family: 'Tajawal', sans-serif;
|
23 |
+
background: #f5f7fa;
|
24 |
+
padding: 20px;
|
25 |
+
color: var(--dark);
|
26 |
+
line-height: 1.6;
|
27 |
+
}
|
28 |
+
|
29 |
+
.container {
|
30 |
+
max-width: 500px;
|
31 |
+
margin: 0 auto;
|
32 |
+
background: white;
|
33 |
+
padding: 25px;
|
34 |
+
border-radius: 12px;
|
35 |
+
box-shadow: var(--shadow);
|
36 |
+
transition: all 0.3s ease;
|
37 |
+
}
|
38 |
+
|
39 |
+
h1 {
|
40 |
+
color: var(--primary);
|
41 |
+
text-align: center;
|
42 |
+
margin-bottom: 25px;
|
43 |
+
font-weight: 700;
|
44 |
+
font-size: 1.8rem;
|
45 |
+
}
|
46 |
+
|
47 |
+
.form-group {
|
48 |
+
margin-bottom: 20px;
|
49 |
+
animation: fadeIn 0.5s ease;
|
50 |
+
}
|
51 |
+
|
52 |
+
@keyframes fadeIn {
|
53 |
+
from { opacity: 0; transform: translateY(10px); }
|
54 |
+
to { opacity: 1; transform: translateY(0); }
|
55 |
+
}
|
56 |
+
|
57 |
+
label {
|
58 |
+
display: block;
|
59 |
+
margin-bottom: 8px;
|
60 |
+
font-weight: 500;
|
61 |
+
color: var(--dark);
|
62 |
+
font-size: 1rem;
|
63 |
+
}
|
64 |
+
|
65 |
+
select, input, button {
|
66 |
+
width: 100%;
|
67 |
+
padding: 12px 15px;
|
68 |
+
border: 1px solid var(--border);
|
69 |
+
border-radius: 8px;
|
70 |
+
font-family: 'Tajawal', sans-serif;
|
71 |
+
font-size: 1rem;
|
72 |
+
transition: all 0.3s;
|
73 |
+
box-sizing: border-box;
|
74 |
+
}
|
75 |
+
|
76 |
+
select:focus, input:focus {
|
77 |
+
outline: none;
|
78 |
+
border-color: var(--primary);
|
79 |
+
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2);
|
80 |
+
}
|
81 |
+
|
82 |
+
button {
|
83 |
+
background: var(--primary);
|
84 |
+
color: white;
|
85 |
+
border: none;
|
86 |
+
cursor: pointer;
|
87 |
+
font-weight: 500;
|
88 |
+
margin-top: 10px;
|
89 |
+
}
|
90 |
+
|
91 |
+
button:hover {
|
92 |
+
background: var(--secondary);
|
93 |
+
transform: translateY(-2px);
|
94 |
+
box-shadow: var(--shadow);
|
95 |
+
}
|
96 |
+
|
97 |
+
button:active {
|
98 |
+
transform: translateY(0);
|
99 |
+
}
|
100 |
+
|
101 |
+
.small-btn {
|
102 |
+
padding: 8px 12px;
|
103 |
+
font-size: 0.9rem;
|
104 |
+
margin: 5px;
|
105 |
+
display: inline-block;
|
106 |
+
width: auto;
|
107 |
+
}
|
108 |
+
|
109 |
+
#verification-section, #result, #service-field, #amount-field, #execute-btn {
|
110 |
+
display: none;
|
111 |
+
}
|
112 |
+
|
113 |
+
hr {
|
114 |
+
margin: 20px 0;
|
115 |
+
border: none;
|
116 |
+
border-top: 1px solid var(--border);
|
117 |
+
}
|
118 |
+
|
119 |
+
#overlay {
|
120 |
+
display: none;
|
121 |
+
position: fixed;
|
122 |
+
top: 0;
|
123 |
+
left: 0;
|
124 |
+
width: 100%;
|
125 |
+
height: 100%;
|
126 |
+
background: rgba(0,0,0,0.5);
|
127 |
+
z-index: 999;
|
128 |
+
backdrop-filter: blur(3px);
|
129 |
+
}
|
130 |
+
|
131 |
+
#result {
|
132 |
+
display: none;
|
133 |
+
position: fixed;
|
134 |
+
top: 50%;
|
135 |
+
left: 50%;
|
136 |
+
transform: translate(-50%, -50%);
|
137 |
+
width: 90%;
|
138 |
+
max-width: 400px;
|
139 |
+
background: white;
|
140 |
+
padding: 25px;
|
141 |
+
border-radius: 12px;
|
142 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
143 |
+
z-index: 1000;
|
144 |
+
animation: popIn 0.4s ease;
|
145 |
+
}
|
146 |
+
|
147 |
+
@keyframes popIn {
|
148 |
+
0% { transform: translate(-50%, -50%) scale(0.8); opacity: 0; }
|
149 |
+
100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
|
150 |
+
}
|
151 |
+
|
152 |
+
#result h2 {
|
153 |
+
color: var(--success);
|
154 |
+
font-size: 1.5rem;
|
155 |
+
text-align: center;
|
156 |
+
margin-top: 0;
|
157 |
+
margin-bottom: 20px;
|
158 |
+
}
|
159 |
+
|
160 |
+
.close-btn {
|
161 |
+
background: var(--success);
|
162 |
+
padding: 10px 15px;
|
163 |
+
float: right;
|
164 |
+
}
|
165 |
+
|
166 |
+
.transaction-detail {
|
167 |
+
margin-bottom: 10px;
|
168 |
+
font-size: 1rem;
|
169 |
+
}
|
170 |
+
|
171 |
+
.transaction-detail strong {
|
172 |
+
color: var(--dark);
|
173 |
+
font-weight: 500;
|
174 |
+
}
|
175 |
+
|
176 |
+
.balance-display {
|
177 |
+
margin-top: 15px;
|
178 |
+
padding-top: 15px;
|
179 |
+
border-top: 1px dashed var(--border);
|
180 |
+
font-weight: bold;
|
181 |
+
}
|
182 |
+
|
183 |
+
.low-balance {
|
184 |
+
color: var(--warning);
|
185 |
+
}
|
186 |
+
|
187 |
+
.normal-balance {
|
188 |
+
color: var(--success);
|
189 |
+
}
|
190 |
+
|
191 |
+
.qr-container {
|
192 |
+
text-align: center;
|
193 |
+
margin: 15px 0;
|
194 |
+
}
|
195 |
+
|
196 |
+
.progress-steps {
|
197 |
+
display: flex;
|
198 |
+
justify-content: space-between;
|
199 |
+
margin-bottom: 20px;
|
200 |
+
position: relative;
|
201 |
+
}
|
202 |
+
|
203 |
+
.progress-steps::before {
|
204 |
+
content: '';
|
205 |
+
position: absolute;
|
206 |
+
top: 15px;
|
207 |
+
left: 0;
|
208 |
+
right: 0;
|
209 |
+
height: 2px;
|
210 |
+
background: var(--border);
|
211 |
+
z-index: 1;
|
212 |
+
}
|
213 |
+
|
214 |
+
.step {
|
215 |
+
position: relative;
|
216 |
+
z-index: 2;
|
217 |
+
text-align: center;
|
218 |
+
width: 30px;
|
219 |
+
height: 30px;
|
220 |
+
line-height: 30px;
|
221 |
+
border-radius: 50%;
|
222 |
+
background: var(--border);
|
223 |
+
color: var(--gray);
|
224 |
+
font-weight: bold;
|
225 |
+
}
|
226 |
+
|
227 |
+
.step.active {
|
228 |
+
background: var(--primary);
|
229 |
+
color: white;
|
230 |
+
}
|
231 |
+
|
232 |
+
.step.completed {
|
233 |
+
background: var(--success);
|
234 |
+
color: white;
|
235 |
+
}
|
236 |
+
|
237 |
+
.step-label {
|
238 |
+
display: block;
|
239 |
+
margin-top: 5px;
|
240 |
+
font-size: 0.8rem;
|
241 |
+
color: var(--gray);
|
242 |
+
}
|
243 |
+
|
244 |
+
.verification-option {
|
245 |
+
margin-bottom: 20px;
|
246 |
+
padding: 15px;
|
247 |
+
border-radius: 8px;
|
248 |
+
background: var(--light);
|
249 |
+
animation: fadeIn 0.5s ease;
|
250 |
+
}
|
251 |
+
|
252 |
+
.verification-option h3 {
|
253 |
+
margin-top: 0;
|
254 |
+
color: var(--dark);
|
255 |
+
font-size: 1rem;
|
256 |
+
}
|
257 |
+
|
258 |
+
.custom-select {
|
259 |
+
position: relative;
|
260 |
+
}
|
261 |
+
|
262 |
+
.custom-select select {
|
263 |
+
appearance: none;
|
264 |
+
-webkit-appearance: none;
|
265 |
+
-moz-appearance: none;
|
266 |
+
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
267 |
+
background-repeat: no-repeat;
|
268 |
+
background-position: right 10px center;
|
269 |
+
background-size: 15px;
|
270 |
+
padding-right: 35px;
|
271 |
+
}
|
272 |
+
</style>
|
273 |
+
</head>
|
274 |
+
<body>
|
275 |
+
<div id="overlay"></div>
|
276 |
+
<div class="container">
|
277 |
+
<h1>نظام الدفع متعدد المحافظ</h1>
|
278 |
+
|
279 |
+
<div class="progress-steps">
|
280 |
+
<div class="step active" id="step1">1</div>
|
281 |
+
<div class="step" id="step2">2</div>
|
282 |
+
<div class="step" id="step3">3</div>
|
283 |
+
</div>
|
284 |
+
|
285 |
+
<div class="form-group custom-select">
|
286 |
+
<label>من محفظتي:</label>
|
287 |
+
<select id="from-wallet">
|
288 |
+
<option value="jawali">جوالي (1000 ر.ي)</option>
|
289 |
+
<option value="kash">كاش (500 ر.ي)</option>
|
290 |
+
<option value="moneymobile">موبايل موني (0 ر.ي)</option>
|
291 |
+
<option value="jeep">جيب (0 ر.ي)</option>
|
292 |
+
<option value="shaamil">شامل (0 ر.ي)</option>
|
293 |
+
<option value="yemenpay">يمن والت (0 ر.ي)</option>
|
294 |
+
</select>
|
295 |
+
</div>
|
296 |
+
|
297 |
+
<div class="form-group custom-select">
|
298 |
+
<label>إلى محفظة:</label>
|
299 |
+
<select id="to-wallet">
|
300 |
+
<option value="">-- اختر المحفظة المستقبلة --</option>
|
301 |
+
<option value="jawali">جوالي</option>
|
302 |
+
<option value="kash">كاش</option>
|
303 |
+
<option value="moneymobile">موبايل موني</option>
|
304 |
+
<option value="jeep">جيب</option>
|
305 |
+
<option value="shaamil">شامل</option>
|
306 |
+
<option value="yemenpay">يمن والت</option>
|
307 |
+
</select>
|
308 |
+
</div>
|
309 |
+
|
310 |
+
<div class="form-group">
|
311 |
+
<label>رقم النقطة / هاتف المستقبل:</label>
|
312 |
+
<input type="text" id="point-number" placeholder="أدخل رقم النقطة أو الهاتف">
|
313 |
+
</div>
|
314 |
+
|
315 |
+
<div class="form-group" id="service-field">
|
316 |
+
<label>الخدمة:</label>
|
317 |
+
<select id="service" class="custom-select">
|
318 |
+
<option value="">-- اختر الخدمة --</option>
|
319 |
+
<option value="تسديد باقة اتصالات">تسديد باقة اتصالات</option>
|
320 |
+
<option value="فاتورة مياه">فاتورة مياه</option>
|
321 |
+
<option value="فاتورة كهرباء">فاتورة كهرباء</option>
|
322 |
+
<option value="مشتريات سوبر ماركت">مشتريات سوبر ماركت</option>
|
323 |
+
</select>
|
324 |
+
</div>
|
325 |
+
|
326 |
+
<div class="form-group" id="amount-field">
|
327 |
+
<label>المبلغ (ر.ي):</label>
|
328 |
+
<input type="number" id="amount" placeholder="أدخل المبلغ">
|
329 |
+
</div>
|
330 |
+
|
331 |
+
<button id="execute-btn">تنفيذ الدفع</button>
|
332 |
+
|
333 |
+
<div id="verification-section">
|
334 |
+
<div class="verification-option">
|
335 |
+
<h3>1. الرمز الثابت (1234):</h3>
|
336 |
+
<input type="password" id="verify-code" placeholder="أدخل الرمز">
|
337 |
+
<button class="small-btn" onclick="confirmVerification()">تأكيد الرمز</button>
|
338 |
+
</div>
|
339 |
+
|
340 |
+
<div class="verification-option">
|
341 |
+
<h3>2. مصادقة البصمة:</h3>
|
342 |
+
<button class="small-btn" onclick="registerFingerprint()">تسجيل البصمة</button>
|
343 |
+
<button class="small-btn" onclick="verifyFingerprint()">التحقق بالبصمة</button>
|
344 |
+
</div>
|
345 |
+
|
346 |
+
<div class="verification-option">
|
347 |
+
<h3>3. QR للتأكيد:</h3>
|
348 |
+
<div class="qr-container">
|
349 |
+
<a href="https://example.com/confirm?code=1234" target="_blank">
|
350 |
+
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://example.com/confirm?code=1234" alt="QR Code">
|
351 |
+
</a>
|
352 |
+
</div>
|
353 |
+
</div>
|
354 |
+
</div>
|
355 |
+
</div>
|
356 |
+
|
357 |
+
<div id="result">
|
358 |
+
<h2>تمت العملية بنجاح</h2>
|
359 |
+
<div id="transaction-details">
|
360 |
+
<div class="transaction-detail">الخدمة: <strong id="detail-service"></strong></div>
|
361 |
+
<div class="transaction-detail">المبلغ المستحق: <strong id="detail-amount"></strong></div>
|
362 |
+
<div class="transaction-detail">مبلغ العمولة: <strong id="detail-fee"></strong></div>
|
363 |
+
<div class="transaction-detail">المبلغ المخصوم: <strong id="detail-total"></strong></div>
|
364 |
+
<div class="transaction-detail" id="detail-transfer"></div>
|
365 |
+
<div class="transaction-detail">رقم النقطة / الهاتف: <strong id="detail-point"></strong></div>
|
366 |
+
<div class="transaction-detail">الرصيد المتبقي: <strong id="detail-balance"></strong></div>
|
367 |
+
<div class="balance-display" id="current-balance-display"></div>
|
368 |
+
</div>
|
369 |
+
<button class="close-btn" onclick="closeResult()">إغلاق</button>
|
370 |
+
</div>
|
371 |
+
|
372 |
+
<script>
|
373 |
+
// Source wallet balances
|
374 |
+
const wallets = {
|
375 |
+
jawali: {balance: 1000, name: "جوالي"},
|
376 |
+
kash: {balance: 500, name: "كاش"},
|
377 |
+
moneymobile: {balance: 0, name: "Mobile Money"},
|
378 |
+
jeep: {balance: 0, name: "Jeep"},
|
379 |
+
shaamil: {balance: 0, name: "شامل"},
|
380 |
+
yemenpay: {balance: 0, name: "يمن والت"}
|
381 |
+
};
|
382 |
+
|
383 |
+
// Wallet display names
|
384 |
+
const walletDisplayNames = {
|
385 |
+
jawali: "جوالي",
|
386 |
+
kash: "كاش",
|
387 |
+
moneymobile: "Mobile Money",
|
388 |
+
jeep: "Jeep",
|
389 |
+
shaamil: "شامل",
|
390 |
+
yemenpay: "يمن والت"
|
391 |
+
};
|
392 |
+
|
393 |
+
let storedCredential = null;
|
394 |
+
let currentStep = 1;
|
395 |
+
|
396 |
+
// Initialize wallet balances display
|
397 |
+
function initWalletBalances() {
|
398 |
+
const fromWalletSelect = document.getElementById("from-wallet");
|
399 |
+
const options = fromWalletSelect.options;
|
400 |
+
|
401 |
+
for (let i = 0; i < options.length; i++) {
|
402 |
+
const walletId = options[i].value;
|
403 |
+
if (wallets[walletId]) {
|
404 |
+
options[i].text = `${wallets[walletId].name} (${wallets[walletId].balance} ر.ي)`;
|
405 |
+
}
|
406 |
+
}
|
407 |
+
}
|
408 |
+
|
409 |
+
// Dynamic form navigation
|
410 |
+
document.getElementById("point-number").addEventListener("input", function() {
|
411 |
+
if (this.value.trim()) {
|
412 |
+
document.getElementById("service-field").style.display = "block";
|
413 |
+
updateStep(2);
|
414 |
+
} else {
|
415 |
+
document.getElementById("service-field").style.display = "none";
|
416 |
+
updateStep(1);
|
417 |
+
}
|
418 |
+
});
|
419 |
+
|
420 |
+
document.getElementById("service").addEventListener("change", function() {
|
421 |
+
if (this.value) {
|
422 |
+
document.getElementById("amount-field").style.display = "block";
|
423 |
+
updateStep(3);
|
424 |
+
} else {
|
425 |
+
document.getElementById("amount-field").style.display = "none";
|
426 |
+
updateStep(2);
|
427 |
+
}
|
428 |
+
});
|
429 |
+
|
430 |
+
document.getElementById("amount").addEventListener("input", function() {
|
431 |
+
if (+this.value > 0) {
|
432 |
+
document.getElementById("execute-btn").style.display = "block";
|
433 |
+
} else {
|
434 |
+
document.getElementById("execute-btn").style.display = "none";
|
435 |
+
}
|
436 |
+
});
|
437 |
+
|
438 |
+
document.getElementById("execute-btn").addEventListener("click", startVerification);
|
439 |
+
|
440 |
+
function updateStep(step) {
|
441 |
+
currentStep = step;
|
442 |
+
document.querySelectorAll('.step').forEach((el, index) => {
|
443 |
+
if (index + 1 < step) {
|
444 |
+
el.classList.remove('active');
|
445 |
+
el.classList.add('completed');
|
446 |
+
} else if (index + 1 === step) {
|
447 |
+
el.classList.add('active');
|
448 |
+
el.classList.remove('completed');
|
449 |
+
} else {
|
450 |
+
el.classList.remove('active', 'completed');
|
451 |
+
}
|
452 |
+
});
|
453 |
+
}
|
454 |
+
|
455 |
+
function startVerification() {
|
456 |
+
const toWallet = document.getElementById("to-wallet").value;
|
457 |
+
const pt = document.getElementById("point-number").value.trim();
|
458 |
+
const service = document.getElementById("service").value;
|
459 |
+
const amt = +document.getElementById("amount").value;
|
460 |
+
|
461 |
+
if (!toWallet || !pt || !service || !amt) {
|
462 |
+
return alert("يرجى ملء جميع الحقول أولاً");
|
463 |
+
}
|
464 |
+
|
465 |
+
document.getElementById("verification-section").style.display = "block";
|
466 |
+
document.getElementById("execute-btn").style.display = "none";
|
467 |
+
window.scrollTo(0, document.body.scrollHeight);
|
468 |
+
}
|
469 |
+
|
470 |
+
function confirmVerification() {
|
471 |
+
if (document.getElementById("verify-code").value === "1234") {
|
472 |
+
doPayment();
|
473 |
+
} else {
|
474 |
+
alert("الرمز غير صحيح");
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
async function registerFingerprint() {
|
479 |
+
if (!window.PublicKeyCredential) return alert("المتصفح لا يدعم WebAuthn");
|
480 |
+
try {
|
481 |
+
const cred = await navigator.credentials.create({
|
482 |
+
publicKey: {
|
483 |
+
challenge: new Uint8Array(32),
|
484 |
+
rp: {name: "Payment System"},
|
485 |
+
user: {id: Uint8Array.from([1,2,3,4]), name: "user", displayName: "User"},
|
486 |
+
pubKeyCredParams: [{type: "public-key", alg: -7}],
|
487 |
+
timeout: 60000,
|
488 |
+
attestation: "none"
|
489 |
+
}
|
490 |
+
});
|
491 |
+
storedCredential = cred;
|
492 |
+
alert("تم تسجيل بصمتك بنجاح");
|
493 |
+
} catch {
|
494 |
+
alert("فشل تسجيل البصمة");
|
495 |
+
}
|
496 |
+
}
|
497 |
+
|
498 |
+
async function verifyFingerprint() {
|
499 |
+
if (!storedCredential) return alert("الرجاء تسجيل البصمة أولاً");
|
500 |
+
try {
|
501 |
+
await navigator.credentials.get({
|
502 |
+
publicKey: {
|
503 |
+
challenge: new Uint8Array(32),
|
504 |
+
allowCredentials: [{type: "public-key", id: storedCredential.rawId}],
|
505 |
+
timeout: 60000,
|
506 |
+
userVerification: "preferred"
|
507 |
+
}
|
508 |
+
});
|
509 |
+
alert("تم التحقق بالبصمة");
|
510 |
+
doPayment();
|
511 |
+
} catch {
|
512 |
+
alert("فشل التحقق بالبصمة");
|
513 |
+
}
|
514 |
+
}
|
515 |
+
|
516 |
+
function doPayment() {
|
517 |
+
const from = document.getElementById("from-wallet").value;
|
518 |
+
const to = document.getElementById("to-wallet").value;
|
519 |
+
const service = document.getElementById("service").value;
|
520 |
+
const pt = document.getElementById("point-number").value;
|
521 |
+
const amt = +document.getElementById("amount").value;
|
522 |
+
const fee = 20;
|
523 |
+
|
524 |
+
// Check source wallet balance
|
525 |
+
if (wallets[from].balance < amt + fee) {
|
526 |
+
return alert("رصيد غير كافٍ في محفظتك");
|
527 |
+
}
|
528 |
+
|
529 |
+
// Deduct from source wallet
|
530 |
+
wallets[from].balance -= amt + fee;
|
531 |
+
|
532 |
+
// Get display names for wallets
|
533 |
+
const fromDisplay = wallets[from].name;
|
534 |
+
const toDisplay = walletDisplayNames[to] || to;
|
535 |
+
|
536 |
+
// Update transaction details
|
537 |
+
document.getElementById("detail-service").textContent = service;
|
538 |
+
document.getElementById("detail-amount").textContent = amt + " ر.ي";
|
539 |
+
document.getElementById("detail-fee").textContent = fee + " ر.ي";
|
540 |
+
document.getElementById("detail-total").textContent = (amt + fee) + " ر.ي";
|
541 |
+
document.getElementById("detail-transfer").innerHTML =
|
542 |
+
`تم تحويل <strong>${amt} ر.ي</strong> من <strong>${fromDisplay}</strong> إلى <strong>${toDisplay}</strong>`;
|
543 |
+
document.getElementById("detail-point").textContent = pt;
|
544 |
+
document.getElementById("detail-balance").textContent =
|
545 |
+
`${fromDisplay}: ${wallets[from].balance} ر.ي`;
|
546 |
+
|
547 |
+
// Show current balance in a prominent way
|
548 |
+
const balanceDisplay = document.getElementById("current-balance-display");
|
549 |
+
balanceDisplay.innerHTML = `
|
550 |
+
الرصيد المتوفر حالياً: <strong>${wallets[from].balance} ر.ي</strong>
|
551 |
+
`;
|
552 |
+
balanceDisplay.className = wallets[from].balance < 500 ?
|
553 |
+
"balance-display low-balance" : "balance-display normal-balance";
|
554 |
+
|
555 |
+
// Show result
|
556 |
+
document.getElementById("overlay").style.display = "block";
|
557 |
+
document.getElementById("result").style.display = "block";
|
558 |
+
|
559 |
+
// Update the source wallet balance in dropdown
|
560 |
+
updateWalletBalances();
|
561 |
+
}
|
562 |
+
|
563 |
+
// Update wallet balances in dropdown
|
564 |
+
function updateWalletBalances() {
|
565 |
+
const fromWalletSelect = document.getElementById("from-wallet");
|
566 |
+
const options = fromWalletSelect.options;
|
567 |
+
|
568 |
+
for (let i = 0; i < options.length; i++) {
|
569 |
+
const walletId = options[i].value;
|
570 |
+
if (wallets[walletId]) {
|
571 |
+
options[i].text = `${wallets[walletId].name} (${wallets[walletId].balance} ر.ي)`;
|
572 |
+
}
|
573 |
+
}
|
574 |
+
}
|
575 |
+
|
576 |
+
function closeResult() {
|
577 |
+
document.getElementById("result").style.display = "none";
|
578 |
+
document.getElementById("overlay").style.display = "none";
|
579 |
+
document.getElementById("verification-section").style.display = "none";
|
580 |
+
|
581 |
+
// Reset form
|
582 |
+
document.getElementById("to-wallet").value = "";
|
583 |
+
document.getElementById("point-number").value = "";
|
584 |
+
document.getElementById("service").value = "";
|
585 |
+
document.getElementById("amount").value = "";
|
586 |
+
document.getElementById("verify-code").value = "";
|
587 |
+
|
588 |
+
// Reset steps
|
589 |
+
updateStep(1);
|
590 |
+
|
591 |
+
// Update wallet balances in UI
|
592 |
+
updateWalletBalances();
|
593 |
+
}
|
594 |
+
|
595 |
+
// Initialize on page load
|
596 |
+
document.addEventListener("DOMContentLoaded", function() {
|
597 |
+
// Change labels as requested
|
598 |
+
document.querySelector('label[for="from-wallet"]').textContent = "من محفظتي:";
|
599 |
+
document.querySelector('label[for="to-wallet"]').textContent = "إلى محفظة:";
|
600 |
+
|
601 |
+
// Initialize wallet balances display
|
602 |
+
initWalletBalances();
|
603 |
+
});
|
604 |
+
</script>
|
605 |
+
|
606 |
+
<!-- تذييل الصفحة مع حقوق الملكية -->
|
607 |
+
<footer style="text-align: center; margin-top: 20px; color: #666; font-size: 0.9rem; padding: 10px;">
|
608 |
+
<p>المدى للخدمات البرمجية التسويقية والإعلانية</p>
|
609 |
+
<p>المدير العام: المهندس/ محمد المرتضى</p>
|
610 |
+
<p>© <span id="current-year">2023</span> جميع الحقوق محفوظة</p>
|
611 |
+
</footer>
|
612 |
+
|
613 |
+
<script>
|
614 |
+
// Update year automatically
|
615 |
+
document.getElementById('current-year').textContent = new Date().getFullYear();
|
616 |
+
</script>
|
617 |
+
</body>
|
618 |
+
</html>
|