Merge branch 'dev' of github.com:VinistanSS/FCSC into dev

This commit is contained in:
venbaittech 2025-02-21 08:57:06 +05:30
commit bdcdf737a8
34 changed files with 647 additions and 417 deletions

View File

@ -1,5 +1,5 @@
{ {
"projects": { "projects": {
"default": "uae-stats-demo" "default": "fcsc-da580"
} }
} }

View File

@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application <application
android:label="UAE Stats" android:label="UAE Stats"
android:name="${applicationName}" android:name="${applicationName}"
@ -26,6 +27,24 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data android:name="flutter_deeplinking_enabled"
android:value="true"/>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="fcsc-da580.web.app"/>
<data android:scheme="https" />
</intent-filter>
<!-- <intent-filter >-->
<!-- <action android:name="android.intent.action.VIEW" />-->
<!-- <category android:name="android.intent.category.DEFAULT" />-->
<!-- <category android:name="android.intent.category.BROWSABLE" />-->
<!-- <data-->
<!-- android:scheme="https"-->
<!-- android:host="dev-fcsc.web.app"-->
<!-- android:pathPrefix="/" />-->
<!-- </intent-filter>-->
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

View File

@ -0,0 +1,6 @@
package ae.gov.fcsc.frontend;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}

View File

@ -1,5 +0,0 @@
package ae.gov.fcsc.frontend
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,18 +1,6 @@
{ {
"flutter": {
"platforms": {
"dart": {
"lib/firebase_options.dart": {
"projectId": "uae-stats-demo",
"configurations": {
"web": "1:225888316715:web:b96ad37877684cd2dd296e"
}
}
}
}
},
"hosting": { "hosting": {
"public": "build/web", "public": "p",
"ignore": [ "ignore": [
"firebase.json", "firebase.json",
"**/.*", "**/.*",

View File

@ -98,7 +98,7 @@
"failed_to_update_password": "", "failed_to_update_password": "",
"password_update_successfully": "تم تحديث كلمة المرور بنجاح.", "password_update_successfully": "تم تحديث كلمة المرور بنجاح.",
"new_password_not_same_as_old": "كلمة المرور الجديدة يجب ألا تكون نفس كلمة المرور القديمة", "new_password_not_same_as_old": "كلمة المرور الجديدة يجب ألا تكون نفس كلمة المرور القديمة",
"password_between_8_to_40": "يجب أن تتراوح كلمة المرور بين 8 و64 حرفًا", "password_between_8_to_40": "يجب أن تتراوح كلمة المرور بين 8 و40 حرفًا",
"new_password_required": "كلمة المرور الجديدة مطلوبة", "new_password_required": "كلمة المرور الجديدة مطلوبة",
"old_password_is_required": "كلمة المرور القديمة مطلوبة", "old_password_is_required": "كلمة المرور القديمة مطلوبة",
"confirm_password_is_required": "تأكيد كلمة المرور مطلوب", "confirm_password_is_required": "تأكيد كلمة المرور مطلوب",

View File

@ -99,7 +99,7 @@
"failed_to_update_password": "", "failed_to_update_password": "",
"password_update_successfully": "Password updated successfully.", "password_update_successfully": "Password updated successfully.",
"new_password_not_same_as_old": "New password must not be the same as the old password", "new_password_not_same_as_old": "New password must not be the same as the old password",
"password_between_8_to_40": "Password must be between 8 and 64 characters", "password_between_8_to_40": "Password must be between 8 and 40 characters",
"new_password_required": "New password is required", "new_password_required": "New password is required",
"old_password_is_required": "Old password is required", "old_password_is_required": "Old password is required",
"confirm_password_is_required": "Confirm password is required", "confirm_password_is_required": "Confirm password is required",

View File

@ -220,14 +220,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
context.pop();
if (widget.keyParam == 'editProfile') {
context.go('/editProfile');
}else
{
context.go('/profile/${widget.userId}');
}
}, },
child: Container( child: Container(
margin: EdgeInsets.only(top: 10,left: 1,bottom: 16,right: 1), // Add margin for positioning margin: EdgeInsets.only(top: 10,left: 1,bottom: 16,right: 1), // Add margin for positioning
@ -249,7 +242,13 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
pathColorWhenOn: Colors.grey.shade300, pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300, pathColorWhenOff: Colors.grey.shade300,
onTap: () { onTap: () {
final formState = _formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale(); ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
formState?.validate();
}
});
}, },
); );
}, },
@ -658,7 +657,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
SizedBox(height: 10), SizedBox(height: 10),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
context.go('/'); context.go('/login');
}, },
style: ButtonStyle( style: ButtonStyle(
shape: WidgetStatePropertyAll( shape: WidgetStatePropertyAll(
@ -697,6 +696,14 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
'Login', 'Login',
'تسجيل الدخول', 'تسجيل الدخول',
), ),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'Roboto',
),
fontSize: 17,
fontWeight: FontWeight.w500,
),
), ),
6.horizontalSpace, 6.horizontalSpace,
const Icon(Icons.chevron_right_outlined,color:Colors.white), const Icon(Icons.chevron_right_outlined,color:Colors.white),

View File

@ -7,12 +7,13 @@ import 'package:go_router/go_router.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/my_theme.dart'; import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/Screens/profilepage.dart'; import 'package:uae_stat/presentation/Screens/profilepage.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../components/my_toggle.dart'; import 'package:uae_stat/presentation/components/my_toggle.dart';
import '../../routes/auth_routes/login_route.dart'; import '../../routes/auth_routes/login_route.dart';
class RegisterScreen extends ConsumerStatefulWidget { class RegisterScreen extends ConsumerStatefulWidget {
@ -120,10 +121,10 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} else if (value.length > 40) { } else if (value.length > 40) {
return 'Must not exceed 40 characters'; return AppLocalizations.of(context)!.only40charac;
} else if (!RegExp(r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$") } else if (!RegExp(r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$")
.hasMatch(value)) { .hasMatch(value)) {
return 'Invalid Characters'; return AppLocalizations.of(context)!.invalid_characters;
} }
return null; return null;
} }
@ -138,7 +139,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} else if (!RegExp(emailRegex).hasMatch(value)) { } else if (!RegExp(emailRegex).hasMatch(value)) {
return 'Invalid Email'; return AppLocalizations.of(context)!.invalid_email;
} }
return null; return null;
} }
@ -157,38 +158,38 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} else if (value.length < 8) { } else if (value.length < 8) {
return 'Password must be at least 8characters'; return AppLocalizations.of(context)!.only8charac;
} }
// Check the length constraint // Check the length constraint
if (value.length < 8 || value.length > 64) { if (value.length < 8 || value.length > 40) {
return 'Password must be between 8 and 64 characters'; return AppLocalizations.of(context)!.password_between_8_to_40;
} }
// Check the regular expression // Check the regular expression
if (!regex.hasMatch(value)) { if (!regex.hasMatch(value)) {
return 'Password contains invalid characters'; return AppLocalizations.of(context)!.password_invalid;
} }
// Track missing constraints // Track missing constraints
List<String> missingConstraints = []; List<String> missingConstraints = [];
if (!hasUppercase.hasMatch(value)) { if (!hasUppercase.hasMatch(value)) {
missingConstraints.add('uppercase letter'); missingConstraints.add(context.translate('uppercase letter','حرف كبير'));
} }
if (!hasLowercase.hasMatch(value)) { if (!hasLowercase.hasMatch(value)) {
missingConstraints.add('lowercase letter'); missingConstraints.add(context.translate('lowercase letter','حرف صغير'));
} }
if (!hasDigit.hasMatch(value)) { if (!hasDigit.hasMatch(value)) {
missingConstraints.add('numeric digit'); missingConstraints.add(context.translate('numeric digit','رقم'));
} }
if (!hasSpecialCharacter.hasMatch(value)) { if (!hasSpecialCharacter.hasMatch(value)) {
missingConstraints.add('special character'); missingConstraints.add(context.translate('special character','رمز خاص'));
} }
// If there are missing constraints, return a consolidated message // If there are missing constraints, return a consolidated message
if (missingConstraints.isNotEmpty) { if (missingConstraints.isNotEmpty) {
return 'At least one ${missingConstraints.join(', ')}'; return context.translate('At least one ${missingConstraints.join(', ')}','${missingConstraints.join(', ')}على الأقل واحد ');
} }
_password = value; // Store the password for confirm password validation _password = value; // Store the password for confirm password validation
@ -203,11 +204,12 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} else if (value.length < 8 || value.length > 40) { } else if (value.length < 8 || value.length > 40) {
return 'Password must be at least 8characters'; return AppLocalizations.of(context)!.password_between_8_to_40;
} else if (!regex.hasMatch(value)) { } else if (!regex.hasMatch(value)) {
return 'Password contains invalid characters'; return AppLocalizations.of(context)!.password_invalid;
} else if (value != _password) { } else if (value != _password) {
return 'Passwords do not match'; return AppLocalizations.of(context)!.password_match;
;
} }
return null; return null;
@ -261,7 +263,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
// Check if the error is due to an invalid or already used email // Check if the error is due to an invalid or already used email
if (e if (e
.toString() .toString()
.contains('The email is invalid or already in use.')) { .contains(AppLocalizations.of(context)!.email_invalid)) {
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -321,6 +323,11 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
} }
} }
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 40,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double screenheight = MediaQuery.of(context).size.height; double screenheight = MediaQuery.of(context).size.height;
@ -376,7 +383,13 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
pathColorWhenOn: Colors.grey.shade300, pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300, pathColorWhenOff: Colors.grey.shade300,
onTap: () { onTap: () {
final formState = _formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale(); ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
formState?.validate();
}
});
}, },
); );
}, },
@ -1224,17 +1237,19 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
height: 10, height: 10,
), ),
Center( Center(
child: Container( // child: Container(
height: screenheight / 16, // height: screenheight / 16,
width: screenwidth / 3, // width: screenwidth / 3,
decoration: BoxDecoration( // decoration: BoxDecoration(
image: DecorationImage( // image: DecorationImage(
image: AssetImage( // image: AssetImage(
"assets/splash_screen/logo.png"), // Background image asset // "assets/splash_screen/logo.png"), // Background image asset
fit: BoxFit.fill, // fit: BoxFit.fill,
// ),
// ),
// )
child: fcscBanner,
), ),
),
)),
SizedBox( SizedBox(
height: 20, height: 20,
), ),

View File

@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:screenshot/screenshot.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
import 'package:uae_stat/config/toast_util.dart'; import 'package:uae_stat/config/toast_util.dart';
@ -17,6 +19,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart'; import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
import '../filters/search_filter_helper.dart'; import '../filters/search_filter_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:share_plus/share_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
class ChartScreen1 extends ConsumerStatefulWidget { class ChartScreen1 extends ConsumerStatefulWidget {
final String dataSets; final String dataSets;
@ -148,17 +155,17 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
}); });
// ToastUtil.showSuccessToast("Added to Bookmark."); // ToastUtil.showSuccessToast("Added to Bookmark.");
ScaffoldMessenger.of(context).showSnackBar( // ScaffoldMessenger.of(context).showSnackBar(
SnackBar( // SnackBar(
content: Text( // content: Text(
AppLocalizations.of(context)!.added_to_Bookmark, // AppLocalizations.of(context)!.added_to_Bookmark,
style: TextStyle( // style: TextStyle(
color: Color(0xFF2F692C), fontWeight: FontWeight.w600), // color: Color(0xFF2F692C), fontWeight: FontWeight.w600),
), // ),
backgroundColor: Color(0xFFD6E9C6), // backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2), // duration: Duration(seconds: 2),
), // ),
); // );
print("Bookmark added: ${response.id}"); print("Bookmark added: ${response.id}");
} catch (e) { } catch (e) {
@ -2127,30 +2134,68 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
color: Colors.white, color: Colors.white,
), ),
), ),
SizedBox(width: 6), SizedBox(width: 5),
Stack( GestureDetector(
children: [ onTap: () async {
// Image.asset( try {
// UaeNumbersAssetPath.shareUae, // Define share content
// color: Colors.white, // Outline color final String shareLink = 'https://fcsc-da580.web.app'; // Your link
// width: 24, // Slightly larger final String shareText = 'Check this out!';
// height: 24, final String svgAssetPath = 'assets/logos/fcsc.png'; // Your SVG asset path
// ),
// Image.asset(
// UaeNumbersAssetPath.shareUae,
// color: Colors.white,
// width: 24,
// height: 24,
// ),
SvgPicture.asset( // Create a controller to capture the SVG as an image
UaeNumbersAssetPath.share, final screenshotController = ScreenshotController();
semanticsLabel: 'share',
// Render the SVG off-screen and capture it as PNG
final svgWidget = SvgPicture.asset(
svgAssetPath,
width: 200, // Adjust size as needed
height: 200,
);
// Capture the SVG as a PNG
final Uint8List? capturedImage = await screenshotController.captureFromWidget(
Material(
child: svgWidget,
),
);
if (capturedImage == null) {
throw Exception('Failed to capture SVG as image');
}
// Save the captured PNG to a temporary file
final directory = await getTemporaryDirectory();
final file = File('${directory.path}/shared_image.png');
await file.writeAsBytes(capturedImage);
// Share the PNG file with text and link
await Share.shareXFiles(
[XFile(file.path)],
text: '$shareText\n$shareLink',
subject: 'Shared Content',
);
// Clean up temporary file
await file.delete();
} catch (e) {
print("Error sharing file: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error sharing: $e')),
);
}
},
child: Container(
width: 24, width: 24,
height: 24, height: 24,
child: Icon(
Icons.share,
color: Colors.white,
size: 24,
semanticLabel: 'Share',
),
),
), ),
],
)
], ],
), ),
SizedBox( SizedBox(

View File

@ -273,7 +273,7 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
// Function to validate the DOB field // Function to validate the DOB field
String? _validateDob(String? value) { String? _validateDob(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
final DateTime selectedDate = _selectedDate!; final DateTime selectedDate = _selectedDate!;
@ -281,14 +281,14 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
// Check if the selected date is in the future // Check if the selected date is in the future
if (selectedDate.isAfter(today)) { if (selectedDate.isAfter(today)) {
return 'Date of birth cannot be in the future'; return context.translate('Date of birth cannot be in the future','لا يمكن أن يكون تاريخ الميلاد في المستقبل');
} }
return null; return null;
} }
String? _validateDropdown(String? value) { String? _validateDropdown(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
return null; return null;
} }
@ -657,7 +657,7 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
enabled: true, enabled: true,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
//RegExp(r"^[a-zA-Z\s]+$"); //RegExp(r"^[a-zA-Z\s]+$");
@ -665,7 +665,7 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$"); r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$");
if (!nameRegex.hasMatch(value)) { if (!nameRegex.hasMatch(value)) {
return 'Invalid Characters'; return AppLocalizations.of(context)!.invalid_characters;
} }
return null; return null;
}, },
@ -912,7 +912,7 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
Padding( Padding(
padding: const EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: Text( child: Text(
'Required', context.translate('Required', 'مطلوب'),
style: TextStyle( style: TextStyle(
color: Color(0xFFb22222), color: Color(0xFFb22222),
fontSize: 12, fontSize: 12,
@ -928,7 +928,7 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
final userId = widget.userId; final userId = widget.userId;
final email = _emailController.text; final email = _emailController.text;
context.go('/createNewPw/$userId/$email'); context.push('/createNewPw/$userId/$email');
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.change_password, AppLocalizations.of(context)!.change_password,

View File

@ -24,6 +24,8 @@ import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_r
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../../Screens/auth_verification/registration.dart'; import '../../Screens/auth_verification/registration.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../components/indicators/locale_provider.dart'; import '../../components/indicators/locale_provider.dart';
import '../../components/my_toggle.dart'; import '../../components/my_toggle.dart';
@ -451,7 +453,7 @@ class LoginRoute extends HookConsumerWidget {
), ),
fontSize: locale?.languageCode == 'ar' ? 12: 18, fontSize: locale?.languageCode == 'ar' ? 12: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
) ),
), ),
6.horizontalSpace, 6.horizontalSpace,
const Icon( const Icon(
@ -473,10 +475,14 @@ class LoginRoute extends HookConsumerWidget {
), ),
validator: (value) { validator: (value) {
final emailRegex =
r'^[a-zA-Z0-9àèìòùÀÈÌÒÙéàç~!#$%^&*()_+=-{}|;,.?<>]+@[a-zA-Z0-9àèìòùÀÈÌÒÙéàç~!#$%^&*()_+=-{}|;,.?<>]+\.[a-zA-Z]{2,}$';
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} }
return FieldValidator.email()(value); else if (!RegExp(emailRegex).hasMatch(value)) {
return AppLocalizations.of(context)!.invalid_email;
} return null;
}, },
imgPath: MiscIconAssetPath.person, imgPath: MiscIconAssetPath.person,
controller: emailCtl, controller: emailCtl,
@ -487,7 +493,7 @@ class LoginRoute extends HookConsumerWidget {
if (text == null || text.isEmpty) { if (text == null || text.isEmpty) {
return context.translate('Required', 'مطلوب'); return context.translate('Required', 'مطلوب');
} else if (text.length < 8) { } else if (text.length < 8) {
return 'The password must be at least 8 characters'; return AppLocalizations.of(context)!.only8charac;
} }
return FieldValidator.password(minLength: 8)(text); return FieldValidator.password(minLength: 8)(text);
}, },
@ -668,7 +674,13 @@ class LoginRoute extends HookConsumerWidget {
pathColorWhenOn: Colors.grey.shade300, pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300, pathColorWhenOff: Colors.grey.shade300,
onTap: () { onTap: () {
final formState = formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale(); ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
formState?.validate();
}
});
}, },
), ),
), ),

View File

@ -109,17 +109,17 @@ class _BookMarkState extends ConsumerState<BookMark> {
'Authorization': adminToken, 'Authorization': adminToken,
}); });
// Show success SnackBar // Show success SnackBar
ScaffoldMessenger.of(context).showSnackBar( // ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( // const SnackBar(
content: Text( // content: Text(
'Removed from Bookmark.', // 'Removed from Bookmark.',
style: TextStyle( // style: TextStyle(
color: Color(0xFF2F692C), fontWeight: FontWeight.w600), // color: Color(0xFF2F692C), fontWeight: FontWeight.w600),
), // ),
backgroundColor: Color(0xFFD6E9C6), // backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2), // duration: Duration(seconds: 2),
), // ),
); // );
final locale = ref.read(localeProvider); final locale = ref.read(localeProvider);
fetchBookmarks(locale?.languageCode ?? 'en'); fetchBookmarks(locale?.languageCode ?? 'en');
// ToastUtil.showSuccessToast("Removed from Bookmark."); // ToastUtil.showSuccessToast("Removed from Bookmark.");
@ -140,90 +140,96 @@ class _BookMarkState extends ConsumerState<BookMark> {
} }
/// Show confirmation dialog before removing bookmark /// Show confirmation dialog before removing bookmark
// void showRemoveBookmarkDialog(id) { void showRemoveBookmarkDialog(id) {
// double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
// showDialog( showDialog(
// context: context, context: context,
// barrierDismissible: false, // User must tap button to dismiss dialog barrierDismissible: false, // User must tap button to dismiss dialog
// builder: (context) => AlertDialog( builder: (context) => AlertDialog(
// shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(5.0), // Rounded corners borderRadius: BorderRadius.circular(5.0), // Rounded corners
// ), ),
// contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
// content: Stack( content: Stack(
// children: [ children: [
// Padding( Padding(
// padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
// child: Column( child: Column(
// mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
// children: [ children: [
// SizedBox( SizedBox(
// height: myheight / 30, height: myheight / 30,
// ), ),
// Text( Text(
// context.translate( context.translate(
// 'Are you sure you want to remove this bookmark?', 'Are you sure you want to remove this bookmark?',
// 'هل أنت متأكد أنك تريد إزالة هذه الإشارة المرجعية؟'), 'هل أنت متأكد أنك تريد إزالة هذه الإشارة المرجعية؟'),
// textAlign: TextAlign.center, textAlign: TextAlign.center,
// style: TextStyle( style: TextStyle(
// fontSize: 18, fontSize: 18,
// color: Color(0xFF898C81), color: Color(0xFF898C81),
// ), ),
// ) )
// ], ],
// ), ),
// ), ),
// ], ],
// ), ),
// actions: [ actions: [
// SizedBox(height: 20), SizedBox(height: 20),
// SizedBox( Row(
// width: 100, // Set the desired width mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// child: TextButton( children: [
// onPressed: () => Navigator.pop(context), SizedBox(
// style: TextButton.styleFrom( width: 100, // Set the desired width
// shape: RoundedRectangleBorder( child: TextButton(
// borderRadius: BorderRadius.circular( onPressed: () => Navigator.pop(context),
// 10.0), // Adjust the radius as needed style: TextButton.styleFrom(
// ), shape: RoundedRectangleBorder(
// side: BorderSide( borderRadius: BorderRadius.circular(
// color: Color(0xFFAA8E83), // Set the outline color 10.0), // Adjust the radius as needed
// width: 1, // Set the border width ),
// ), side: BorderSide(
// ), color: Color(0xFFAA8E83), // Set the outline color
// child: Text( width: 1, // Set the border width
// context.translate('No', 'لا'), ),
// style: TextStyle( ),
// color: Color(0xFFAA8E83), child: Text(
// fontSize: 16, context.translate('No', 'لا'),
// ), style: TextStyle(
// ), color: Color(0xFFAA8E83),
// ), fontSize: 16,
// ), ),
// SizedBox( ),
// width: 100, // Set the desired width ),
// child: TextButton( ),
// onPressed: () { SizedBox(
// Navigator.pop(context); width: 100, // Set the desired width
// removeBookmark(id); // Close dialog child: TextButton(
// }, onPressed: () {
// style: TextButton.styleFrom( Navigator.pop(context);
// backgroundColor: Color(0xFFAA8E83), // Set background color removeBookmark(id); // Close dialog
// foregroundColor: Colors.white, },
// shape: RoundedRectangleBorder( style: TextButton.styleFrom(
// borderRadius: BorderRadius.circular(10.0), // Set text color backgroundColor: Color(0xFFAA8E83), // Set background color
// ), foregroundColor: Colors.white,
// ), shape: RoundedRectangleBorder(
// child: Text( borderRadius: BorderRadius.circular(10.0), // Set text color
// context.translate('Yes', 'نعم'), ),
// style: TextStyle(color: Colors.white, fontSize: 16), ),
// ), child: Text(
// ), context.translate('Yes', 'نعم'),
// ), style: TextStyle(color: Colors.white, fontSize: 16),
// ], ),
// ), ),
// ); ),
// } ],
)
],
),
);
}
String colorToHex(Color color) { String colorToHex(Color color) {
return '0xFF${color.red.toRadixString(16).padLeft(2, '0').toUpperCase()}' return '0xFF${color.red.toRadixString(16).padLeft(2, '0').toUpperCase()}'
@ -329,9 +335,9 @@ class _BookMarkState extends ConsumerState<BookMark> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
'The bookmark is removed successfully', 'Removed from BookMark',
), ),
backgroundColor: Colors.green, backgroundColor: Colors.black,
duration: Duration(seconds: 2), duration: Duration(seconds: 2),
), ),
); );
@ -346,14 +352,35 @@ class _BookMarkState extends ConsumerState<BookMark> {
// GestureDetector( // GestureDetector(
// onTap: () { // onTap: () {
// debugPrint("Icon clicked in: ${data['title']}"); // debugPrint("Icon clicked in: ${data['title']}");
// // showRemoveBookmarkDialog(data['id']); // removeBookmark(data['id']);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// 'The bookmark is removed successfully',
// ),
// backgroundColor: Colors.green,
// duration: Duration(seconds: 2),
// ),
// );
// }, // },
// child: const Icon( // child: const Icon(
// Icons.bookmarks_rounded, // Icons.delete,
// color: Colors.black, // color: Colors.black,
// size: 20, // size: 20,
// ), // ),
// ), // ),
GestureDetector(
onTap: () {
debugPrint("Icon clicked in: ${data['title']}");
showRemoveBookmarkDialog(data['id']);
},
child: const Icon(
Icons.bookmarks_rounded,
color: Colors.black,
size: 20,
),
),
], ],
), ),
Text( Text(

View File

@ -13,6 +13,7 @@ import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:uae_stat/config/my_theme.dart'; import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
@ -298,7 +299,7 @@ class _EditProfileState extends State<EditProfile> {
// Function to validate the DOB field // Function to validate the DOB field
String? _validateDob(String? value) { String? _validateDob(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
final DateTime selectedDate = _selectedDate!; final DateTime selectedDate = _selectedDate!;
@ -306,14 +307,14 @@ class _EditProfileState extends State<EditProfile> {
// Check if the selected date is in the future // Check if the selected date is in the future
if (selectedDate.isAfter(today)) { if (selectedDate.isAfter(today)) {
return 'Date of birth cannot be in the future'; return context.translate('Date of birth cannot be in the future','لا يمكن أن يكون تاريخ الميلاد في المستقبل');
} }
return null; return null;
} }
String? _validateDropdown(String? value) { String? _validateDropdown(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
return null; return null;
} }
@ -544,7 +545,7 @@ class _EditProfileState extends State<EditProfile> {
? NetworkImage( ? NetworkImage(
_avatarUrl) // Load from URL _avatarUrl) // Load from URL
: AssetImage( : AssetImage(
'assets/edit_profile/profile.png') 'assets/edit_profile/profile.jpg')
as ImageProvider, as ImageProvider,
child: _avatarUrl.isNotEmpty child: _avatarUrl.isNotEmpty
? FutureBuilder( ? FutureBuilder(
@ -689,7 +690,7 @@ class _EditProfileState extends State<EditProfile> {
enabled: false, enabled: false,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Required'; return context.translate('Required', 'مطلوب');
} }
//RegExp(r"^[a-zA-Z\s]+$"); //RegExp(r"^[a-zA-Z\s]+$");
@ -697,7 +698,7 @@ class _EditProfileState extends State<EditProfile> {
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$", r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$",
); );
if (!nameRegex.hasMatch(value)) { if (!nameRegex.hasMatch(value)) {
return 'Invalid Characters'; return AppLocalizations.of(context)!.invalid_characters;
} }
return null; return null;
}, },
@ -956,7 +957,7 @@ class _EditProfileState extends State<EditProfile> {
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
final email = _emailController.text; final email = _emailController.text;
context.go( context.push(
'/createNewPw/$userId/$email?key=editProfile'); '/createNewPw/$userId/$email?key=editProfile');
}, },
child: Text( child: Text(

View File

@ -3,18 +3,11 @@ import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
class aboutFCSC extends StatelessWidget { class aboutFCSC extends StatelessWidget {
const aboutFCSC({super.key}); const aboutFCSC({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
return BaseScaffold( return BaseScaffold(
showDivider: true, showDivider: true,
showBackButton: true, showBackButton: true,
@ -86,15 +79,10 @@ class aboutFCSC extends StatelessWidget {
], ],
), ),
), ),
SizedBox(height: MediaQuery.of(context).size.height*0.15),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox( SizedBox(
height: 25, height: 20,
), ),
]), ],),
), ),
), ),
); );

View File

@ -3,18 +3,12 @@ import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
class GetStarted extends StatelessWidget { class GetStarted extends StatelessWidget {
const GetStarted({super.key}); const GetStarted({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
return Theme( return Theme(
data: ThemeData( data: ThemeData(
fontFamily: 'Roboto', // Set default font family for this page fontFamily: 'Roboto', // Set default font family for this page
@ -84,12 +78,7 @@ class GetStarted extends StatelessWidget {
SizedBox( SizedBox(
height:MediaQuery.of(context).size.height * 0.12, height:MediaQuery.of(context).size.height * 0.12,
), ),
Align( SizedBox(height: 20,),
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(height: 25,),
], ],
), ),
), ),

View File

@ -6,11 +6,7 @@ import '../../../custom_drawer_routes.dart';
class AppFeatures extends StatelessWidget { class AppFeatures extends StatelessWidget {
AppFeatures({super.key}); AppFeatures({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -205,12 +201,7 @@ class AppFeatures extends StatelessWidget {
], ],
), ),
), ),
SizedBox(height: 25,), SizedBox(height: 20,),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(height: 25,),
], ],
), ),
), ),

View File

@ -1,16 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart';
import '../../../custom_drawer_routes.dart';
class ChangeMyPassword extends StatelessWidget { class ChangeMyPassword extends StatelessWidget {
ChangeMyPassword({super.key}); const ChangeMyPassword({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -116,7 +109,7 @@ class ChangeMyPassword extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF414042), color: Color(0xFF414042),
fontFamily: 'Roboto' fontFamily: 'Roboto',
),), ),),
TextSpan(text:context.translate( TextSpan(text:context.translate(
' link at the bottom.', ' link at the bottom.',
@ -295,9 +288,9 @@ class ChangeMyPassword extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF414042), color: Color(0xFF414042),
fontFamily: 'Roboto' fontFamily: 'Roboto',
),), ),),
TextSpan(text: context.translate('button',''),) TextSpan(text: context.translate('button',''),),
], ],
), ),
), ),
@ -360,14 +353,7 @@ class ChangeMyPassword extends StatelessWidget {
], ],
), ),
), ),
SizedBox(height: 20,),
SizedBox(height: 25,),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(height: 25,),
], ],
), ),

View File

@ -1,16 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart';
import '../../../custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
class EditMyProfile extends StatelessWidget { class EditMyProfile extends StatelessWidget {
EditMyProfile({super.key}); const EditMyProfile({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -457,14 +453,7 @@ class EditMyProfile extends StatelessWidget {
], ],
), ),
), ),
SizedBox(height: 20,),
SizedBox(height: 25,),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(height: 25,),
], ],
), ),

View File

@ -1,16 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart';
import '../../../custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
class HowUseTheApp extends StatelessWidget { class HowUseTheApp extends StatelessWidget {
HowUseTheApp({super.key}); const HowUseTheApp({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -203,14 +199,7 @@ class HowUseTheApp extends StatelessWidget {
], ],
), ),
SizedBox( SizedBox(
height: 25, height: 20,
),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(
height: 25,
), ),
], ],
), ),

View File

@ -1,16 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import '../../../custom_drawer_routes.dart';
class Purpose extends StatelessWidget { class Purpose extends StatelessWidget {
Purpose({super.key}); const Purpose({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -151,14 +145,7 @@ class Purpose extends StatelessWidget {
], ],
), ),
SizedBox( SizedBox(
height: 40, height: 20,
),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(
height: 25,
), ),
], ],
), ),

View File

@ -1,16 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import '../../../custom_drawer_routes.dart';
class StayUpdate extends StatelessWidget { class StayUpdate extends StatelessWidget {
StayUpdate({super.key}); const StayUpdate({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -91,14 +86,7 @@ class StayUpdate extends StatelessWidget {
], ],
), ),
SizedBox( SizedBox(
height: 150, height: 20,
),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(
height: 25,
), ),
], ],
), ),

View File

@ -1,16 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart';
import '../../../custom_drawer_routes.dart';
class WhoUseTheApp extends StatelessWidget { class WhoUseTheApp extends StatelessWidget {
WhoUseTheApp({super.key}); const WhoUseTheApp({super.key});
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@ -141,15 +135,9 @@ class WhoUseTheApp extends StatelessWidget {
), ),
], ],
), ),
SizedBox( SizedBox(
height: 50, height: 20,
),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(
height: 25,
), ),
], ],
), ),

View File

@ -3,18 +3,12 @@ import 'package:go_router/go_router.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
class aboutTheApp extends StatelessWidget { class aboutTheApp extends StatelessWidget {
const aboutTheApp({super.key}); const aboutTheApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
return BaseScaffold( return BaseScaffold(
showDivider: true, showDivider: true,
showBackButton: true, showBackButton: true,
@ -67,7 +61,7 @@ class aboutTheApp extends StatelessWidget {
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
color: Colors.grey.shade300, color: Colors.grey.shade300,
width: 0.5), // Grey bottom line width: 0.5,), // Grey bottom line
), ),
), ),
child: ListTile( child: ListTile(
@ -87,17 +81,12 @@ class aboutTheApp extends StatelessWidget {
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
shape: Border( shape: Border(
bottom: BorderSide( bottom: BorderSide(
color: Colors.black, width: 3), // Grey bottom line color: Colors.black, width: 3,), // Grey bottom line
), ),
), ),
), ),
Spacer(),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox( SizedBox(
height: 25, height: 20,
), ),
], ],
), ),

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
class FAQPage extends StatefulWidget { class FAQPage extends StatefulWidget {
@ -12,11 +10,7 @@ class FAQPage extends StatefulWidget {
} }
class _FAQPageState extends State<FAQPage> { class _FAQPageState extends State<FAQPage> {
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseScaffold( return BaseScaffold(
@ -44,14 +38,7 @@ class _FAQPageState extends State<FAQPage> {
radius: Radius.circular(10), radius: Radius.circular(10),
interactive: true, interactive: true,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: QuestionAnswerScrollView(),
children: [
QuestionAnswerScrollView(),
SizedBox(height: 25,),
fcscBanner,
SizedBox(height: 25,),
],
),
), ),
), ),
), ),
@ -83,24 +70,24 @@ class QuestionAnswerScrollView extends StatelessWidget {
text: text:
'Yes, the app is strictly for registered and approved users. To access the data:\n\n', 'Yes, the app is strictly for registered and approved users. To access the data:\n\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: text:
' 1. You need to sign up using the Register Now option on the login screen.\n', ' 1. You need to sign up using the Register Now option on the login screen.\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: ' 2. Your registration must be approved by the admin.\n', text: ' 2. Your registration must be approved by the admin.\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: text:
" 3. Once approved, you can log in to access the app's features and datasets.\n\nGuest access is not available for this application.\n ", " 3. Once approved, you can log in to access the app's features and datasets.\n\nGuest access is not available for this application.\n ",
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
], ],
), ),
@ -112,24 +99,24 @@ class QuestionAnswerScrollView extends StatelessWidget {
text: text:
'نعم، التطبيق مخصص فقط للمستخدمين المسجلين والمعتمدين. للوصول إلى البيانات:\n\n', 'نعم، التطبيق مخصص فقط للمستخدمين المسجلين والمعتمدين. للوصول إلى البيانات:\n\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: text:
' 1. يجب عليك التسجيل باستخدام خيار "التسجيل الآن" في شاشة تسجيل الدخول.\n', ' 1. يجب عليك التسجيل باستخدام خيار "التسجيل الآن" في شاشة تسجيل الدخول.\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: ' 2. يجب أن يتم الموافقة على تسجيلك من قبل المسؤول.\n', text: ' 2. يجب أن يتم الموافقة على تسجيلك من قبل المسؤول.\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
TextSpan( TextSpan(
text: text:
" بعد الموافقة، يمكنك تسجيل الدخول للوصول إلى ميزات التطبيق وبياناته.\n\n لا يتوفر الوصول للزوار في هذا التطبيق.\n", ' بعد الموافقة، يمكنك تسجيل الدخول للوصول إلى ميزات التطبيق وبياناته.\n\n لا يتوفر الوصول للزوار في هذا التطبيق.\n',
style: TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 16, fontFamily: 'Roboto'), color: Colors.grey, fontSize: 16, fontFamily: 'Roboto',),
), ),
], ],
), ),
@ -140,7 +127,7 @@ class QuestionAnswerScrollView extends StatelessWidget {
'question-ar': 'كيف يمكنني التنقل عبر التطبيق؟', 'question-ar': 'كيف يمكنني التنقل عبر التطبيق؟',
'answer': 'answer':
'• Use the main categories on the homepage (e.g., Economy, Environment) to access subcategories and detailed KPIs.\n• Drill down into specific metrics or graphical views by tapping on a KPI.\n• Access additional features like Bookmarks or Profile through the navigation bar.\n', '• Use the main categories on the homepage (e.g., Economy, Environment) to access subcategories and detailed KPIs.\n• Drill down into specific metrics or graphical views by tapping on a KPI.\n• Access additional features like Bookmarks or Profile through the navigation bar.\n',
'answer-ar': '•استخدم الفئات الرئيسية الموجودة على الصفحة الرئيسية (مثل: الاقتصاد، البيئة) للوصول إلى الفئات الفرعية ومؤشرات الأداء الرئيسية التفصيلية •\n\ قم بالتعمق في مؤشرات معينة أو عرض الرسوم البيانية من خلال النقر على مؤشر الأداء الرئيسي (KPI). \n • للوصول إلى ميزات إضافية مثل الإشارات المرجعية أو الملف الشخصي، استخدم شريط التنقل.\n', 'answer-ar': '•استخدم الفئات الرئيسية الموجودة على الصفحة الرئيسية (مثل: الاقتصاد، البيئة) للوصول إلى الفئات الفرعية ومؤشرات الأداء الرئيسية التفصيلية •\n قم بالتعمق في مؤشرات معينة أو عرض الرسوم البيانية من خلال النقر على مؤشر الأداء الرئيسي (KPI). \n • للوصول إلى ميزات إضافية مثل الإشارات المرجعية أو الملف الشخصي، استخدم شريط التنقل.\n',
}, },
{ {
'question': 'What types of data are available?', 'question': 'What types of data are available?',

View File

@ -11,11 +11,7 @@ class UsingFeatures extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
alignment: Alignment.center,
);
return BaseScaffold( return BaseScaffold(
showDivider: true, showDivider: true,
showBackButton: true, showBackButton: true,
@ -237,14 +233,6 @@ class UsingFeatures extends StatelessWidget {
), ),
), ),
), ),
Spacer(),
Align(
alignment: Alignment.bottomCenter,
child: fcscBanner,
),
SizedBox(
height: 30,
),
], ],
), ),
), ),

View File

@ -14,6 +14,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
import 'package:uae_stat/config/injector.dart'; import 'package:uae_stat/config/injector.dart';
import 'package:uae_stat/domain/repos/t_auth_repo.dart'; import 'package:uae_stat/domain/repos/t_auth_repo.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/data/p_auth_repo.dart'; import 'package:uae_stat/infrastructure/data/p_auth_repo.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/bottom_bar_asset_icon_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/bottom_bar_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/drawer_asset_icon_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/drawer_asset_icon_path.dart';
@ -780,7 +781,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
) )
: Image( : Image(
image: AssetImage( image: AssetImage(
'assets/edit_profile/profile.png', 'assets/edit_profile/profile.jpg',
), ),
fit: BoxFit.cover, fit: BoxFit.cover,
width: double.infinity, width: double.infinity,
@ -792,23 +793,53 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
SizedBox( SizedBox(
width: mywidth / 20, width: mywidth / 20,
), ),
Column( // Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Text(userId ?? 'Loading user...'), // Display userId here
// // Text('Mohammad@fcsc.com')
// if (userId == "guest") ...[
// Text(userName ?? 'Guest User'),
// ] else ...[
// Text(userName ?? 'Loading...'),
// Tooltip(
// message: userEmail ?? 'Loading...',
// child: Text(
// userEmail ?? 'Loading...',
// style: TextStyle(fontSize: 12),
// softWrap: true,
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// ),
// ),
//
// ]
// ],
// ),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Text(userId ?? 'Loading user...'), // Display userId here
// Text('Mohammad@fcsc.com')
if (userId == "guest") ...[ if (userId == "guest") ...[
Text(userName ?? 'Guest User'), Text(userName ?? 'Guest User'),
] else ...[ ] else ...[
Text(userName ?? 'Loading...'), Text(userName ?? 'Loading...'),
Text( Tooltip(
message: userEmail ?? 'Loading...', // Shows full email on hover
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.6, // Adjust width as needed
child: Text(
userEmail ?? 'Loading...', userEmail ?? 'Loading...',
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
// maxLines: 2, // Allow the text to wrap to the next line // softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
), ),
]
], ],
],
),
), ),
], ],
), ),
@ -875,7 +906,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
leading: Icon(Icons.logout), leading: Icon(Icons.logout),
// title: const Text('Logout'), // title: const Text('Logout'),
title: Text(AppLocalizations.of(context)!.logout), title: Text(AppLocalizations.of(context)!.logout),
onTap: () => logout(context), onTap: () => _showLogoutConfirmationDialog(context),
), ),
if (userId == 'guest') if (userId == 'guest')
ListTile( ListTile(
@ -1047,3 +1078,85 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
} }
} }
} }
void _showLogoutConfirmationDialog(context) {
double myheight = MediaQuery.of(context).size.height;
showDialog(
context: context,
barrierDismissible: false, // User must tap a button to dismiss dialog
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), // Rounded corners
),
contentPadding: EdgeInsets.zero,
title: Text(
context.translate('Log Out Confirmation', 'تأكيد تسجيل الخروج'),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
content: Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
context.translate(
'Are you sure you want to log out?',
'هل أنت متأكد أنك تريد تسجيل الخروج؟'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Color(0xFF898C81),
),
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // Space buttons evenly
children: [
SizedBox(
width: 120, // Set button width
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
side: BorderSide(
color: Color(0xFFAA8E83), // Border color
width: 1,
),
),
child: Text(
context.translate('Cancel', 'إلغاء'),
style: TextStyle(
color: Color(0xFFAA8E83),
fontSize: 16,
),
),
),
),
SizedBox(
width: 120, // Set button width
child: TextButton(
onPressed: () {
Navigator.pop(context);
// Add logout function here
},
style: TextButton.styleFrom(
backgroundColor: Color(0xFFAA8E83), // Background color
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
child: Text(
context.translate('Log Out', 'تسجيل الخروج'),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
],
),
SizedBox(height: 10), // Add spacing below buttons
],
),
);
}

33
p/404.html Normal file
View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Not Found</title>
<style media="screen">
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
#message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
@media (max-width: 600px) {
body, #message { margin-top: 0; background: white; box-shadow: none; }
body { border-top: 16px solid #ffa100; }
}
</style>
</head>
<body>
<div id="message">
<h2>404</h2>
<h1>Page Not Found</h1>
<p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
<h3>Why am I seeing this?</h3>
<p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
</div>
</body>
</html>

89
p/index.html Normal file
View File

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Firebase Hosting</title>
<!-- update the version number as needed -->
<script defer src="/__/firebase/11.3.1/firebase-app-compat.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/11.3.1/firebase-auth-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-database-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-firestore-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-functions-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-messaging-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-storage-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-analytics-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-remote-config-compat.js"></script>
<script defer src="/__/firebase/11.3.1/firebase-performance-compat.js"></script>
<!--
initialize the SDK after all desired features are loaded, set useEmulator to false
to avoid connecting the SDK to running emulators.
-->
<script defer src="/__/firebase/init.js?useEmulator=true"></script>
<style media="screen">
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px; border-radius: 3px; }
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
@media (max-width: 600px) {
body, #message { margin-top: 0; background: white; box-shadow: none; }
body { border-top: 16px solid #ffa100; }
}
</style>
</head>
<body>
<div id="message">
<h2>Welcome</h2>
<h1>Firebase Hosting Setup Complete</h1>
<p>You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!</p>
<a target="_blank" href="https://firebase.google.com/docs/hosting/">Open Hosting Documentation</a>
</div>
<p id="load">Firebase SDK Loading&hellip;</p>
<script>
document.addEventListener('DOMContentLoaded', function() {
const loadEl = document.querySelector('#load');
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
// // The Firebase SDK is initialized and available here!
//
// firebase.auth().onAuthStateChanged(user => { });
// firebase.database().ref('/path/to/ref').on('value', snapshot => { });
// firebase.firestore().doc('/foo/bar').get().then(() => { });
// firebase.functions().httpsCallable('yourFunction')().then(() => { });
// firebase.messaging().requestPermission().then(() => { });
// firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
// firebase.analytics(); // call to activate
// firebase.analytics().logEvent('tutorial_completed');
// firebase.performance(); // call to activate
//
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
try {
let app = firebase.app();
let features = [
'auth',
'database',
'firestore',
'functions',
'messaging',
'storage',
'analytics',
'remoteConfig',
'performance',
].filter(feature => typeof app[feature] === 'function');
loadEl.textContent = `Firebase SDK loaded with ${features.join(', ')}`;
} catch (e) {
console.error(e);
loadEl.textContent = 'Error loading the Firebase SDK, check the console.';
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,12 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "ae.gov.fcsc.frontend",
"sha256_cert_fingerprints": [
"3D:BB:9E:B6:3E:80:50:6F:52:EB:73:B8:AD:F9:5F:5A:0A:05:BE:E4:CA:D1:AF:F7:26:2C:56:3F:35:1D:4E:81"
]
}
}
]

View File

@ -1177,6 +1177,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.28.0" version: "0.28.0"
screenshot:
dependency: "direct main"
description:
name: screenshot
sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sdmx: sdmx:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -26,6 +26,7 @@ dependencies:
marquee: ^2.2.3 marquee: ^2.2.3
url_launcher: ^6.3.1 url_launcher: ^6.3.1
path_provider: ^2.1.5 path_provider: ^2.1.5
screenshot: ^3.0.0
# dynamic_layouts: # dynamic_layouts:
# git: # git:
# url: https://github.com/flutter/packages.git # url: https://github.com/flutter/packages.git