session added

This commit is contained in:
venbaittech 2025-02-07 09:53:16 +05:30
parent b275b1a9f3
commit e198cfaa7c
14 changed files with 353 additions and 132 deletions

View File

@ -15,12 +15,12 @@ if (localPropertiesFile.exists()) {
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "9"
flutterVersionCode = "10"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0.8"
flutterVersionName = "1.0.9"
}
def keystorePropertiesFile = rootProject.file("key.properties")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/feedback/errorfb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

BIN
assets/feedback/neutral.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

BIN
assets/feedback/smiling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

BIN
assets/feedback/thumbfb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
abstract class FeedBackAssetPath {
static const _basePath = 'assets/feedback';
static const disappointedfb = '$_basePath/disappointed.png';
static const frowningfb = '$_basePath/frowning.png';
static const neutralfb = '$_basePath/neutral.png';
static const smilingfb = '$_basePath/smiling.png';
static const smilingFacefb = '$_basePath/smilingopenface.png';
static const thumbfb = '$_basePath/thumbfb.png';
static const errorfb = '$_basePath/errorfb.png';
}

View File

@ -1,8 +1,8 @@
{
"feedback_title": "FeedBack",
"feedback_failed_msg": "Your feedback could not be submitted. Please try again",
"feedback_title": "Feedback",
"feedback_failed_msg": "Your feedback could not be \n submitted. Please try again",
"register_title": "Register",
"register_details": "Please enter your details",

View File

@ -241,6 +241,13 @@ class LoginRoute extends HookConsumerWidget {
),
);
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
// maximumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
overlayColor: Colors.white
),
child: Text(
context.translate(
'Forgot Password?',
@ -547,7 +554,7 @@ class LoginRoute extends HookConsumerWidget {
children: [
TextSpan(
text: context.translate(
'Don\'t have an account? ',
'Don\'t have an account?',
'ليس لديك حساب؟',
),
style: const TextStyle(
@ -637,7 +644,7 @@ class LoginRoute extends HookConsumerWidget {
horizontal: listViewHorizontalPadding.toDouble(),
),
children: [
36.verticalSpace,
3.verticalSpace,
Align(
alignment: AlignmentDirectional.topEnd,
child: MyToggle(
@ -651,7 +658,7 @@ class LoginRoute extends HookConsumerWidget {
},
),
),
16.verticalSpace,
36.verticalSpace,
helloAndPleaseLoginTexts,
42.verticalSpace,
form,

View File

@ -8,6 +8,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/feedback_asset_path.dart';
import 'package:uae_stat/infrastructure/services/packages/go_router.dart';
import 'package:uae_stat/presentation/components/my_drawer.dart';
import 'package:mailer/mailer.dart';
@ -28,7 +30,7 @@ class FeedbackForm extends StatefulWidget {
class _FeedbackFormState extends State<FeedbackForm>
with WidgetsBindingObserver {
final _pb =
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
// final _pb =
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
@ -46,32 +48,55 @@ class _FeedbackFormState extends State<FeedbackForm>
dynamic userId;
List<Map<String, dynamic>> get _emojiOptions => [
{
"icon": Icons.sentiment_very_dissatisfied,
"label": context.translate("Terrible", "سئ جدا"), // Translate here
"value": 1,
},
{
"icon": Icons.sentiment_dissatisfied,
"label": context.translate("Bad", "سيء"), // Translate here
"value": 2,
},
{
"icon": Icons.sentiment_neutral,
"label": context.translate("Okay", "مقبول"), // Translate here
"value": 3,
},
{
"icon": Icons.sentiment_satisfied,
"label": context.translate("Good", "جيد"), // Translate here
"value": 4,
},
{
"icon": Icons.sentiment_very_satisfied,
"label": context.translate("Amazing", "مذهل"), // Translate here
"value": 5,
},
];
{
// "icon": Icons.emoji_emotions_rounded,
'icon': Image.asset(
FeedBackAssetPath.disappointedfb,
width: 25,
height: 25,
),
"label": context.translate("Terrible", "سئ جدا"), // Translate here
"value": 1,
},
{
// "icon": Icons.sentiment_dissatisfied,
'icon': Image.asset(
FeedBackAssetPath.frowningfb,
width: 25,
height: 25,
),
"label": context.translate("Bad", "سيء"), // Translate here
"value": 2,
},
{
'icon': Image.asset(
FeedBackAssetPath.neutralfb,
width: 25,
height: 25,
),
"label": context.translate("Okay", "مقبول"), // Translate here
"value": 3,
},
{
'icon': Image.asset(
FeedBackAssetPath.smilingfb,
width: 25,
height: 25,
),
"label": context.translate("Good", "جيد"), // Translate here
"value": 4,
},
{
// "icon": Icons.sentiment_very_satisfied,
'icon': Image.asset(
FeedBackAssetPath.smilingFacefb,
width: 25,
height: 25,
),
"label": context.translate("Amazing", "مذهل"), // Translate here
"value": 5,
},
];
static const int _characterLimit = 1200;
final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$');
@ -208,9 +233,17 @@ class _FeedbackFormState extends State<FeedbackForm>
@override
Widget build(BuildContext context) {
// final uri = Uri.parse(GoRouterState.of(context).uri.toString());
// final userName = uri.queryParameters['userName'] ?? 'Guest User';
final userName = GoRouterState.of(context).extra as String? ?? 'Guest User';
return BaseScaffold(
// title: Text("Feedback"),
title: Text(AppLocalizations.of(context)!.feedback_title),
title: Text(
AppLocalizations.of(context)!.feedback_title,
style: TextStyle(
fontSize: 24, fontFamily: 'Roboto', fontWeight: FontWeight.w500),
),
// appBar: AppBar(
// backgroundColor: const Color(0xFFf8f9ff),
// title: Text(AppLocalizations.of(context)!.feedback_title),
@ -230,13 +263,14 @@ class _FeedbackFormState extends State<FeedbackForm>
// ],
// ),
body: Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.only(
top: 8.0, bottom: 16.0, left: 23.0, right: 23.0),
child: SingleChildScrollView(
child: _isFeedbackSubmitted
? _buildThankYouMessage()
? _buildThankYouMessage(userName)
: _isFeedbackFailed
? _buildFailureMessage()
: _buildFeedbackForm(),
? _buildFailureMessage()
: _buildFeedbackForm(),
),
),
);
@ -260,26 +294,33 @@ class _FeedbackFormState extends State<FeedbackForm>
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Text(
context.translate('How was your experience with us today?',
'كيف كانت تجربتك معنا اليوم؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
Center(
child: FittedBox(
fit: BoxFit
.scaleDown, // This will make the text smaller to fit within the available space
child: Text(
context.translate('How was your experience with us today?',
'كيف كانت تجربتك معنا اليوم؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xff898C81),
),
),
),
const SizedBox(height: 10),
),),
const SizedBox(height: 15),
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(
color: _isSmileySelected ? Colors.transparent : Colors.red,
color:
_isSmileySelected ? Colors.transparent : Colors.transparent,
width: 1.5,
),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(8),
padding: const EdgeInsets.all(0.0),
child: Column(
children: [
Row(
@ -287,6 +328,8 @@ class _FeedbackFormState extends State<FeedbackForm>
children: List.generate(_emojiOptions.length, (index) {
return _buildEmojiButton(
icon: _emojiOptions[index]["icon"],
// icon: _emojiOptions[index]["icon"],
label: _emojiOptions[index]["label"],
index: index,
);
@ -300,28 +343,31 @@ class _FeedbackFormState extends State<FeedbackForm>
'Please rate your experience before submitting your feedback.',
'يرجى تقييم تجربتك قبل تقديم ملاحظاتك.',
),
style: TextStyle(color: Colors.red, fontSize: 14),
style: TextStyle(color: Color(0xFFD83731), fontSize: 14),
),
),
],
),
),
const SizedBox(height: 30),
Text(
context.translate('How good did we do in these aspects?',
'ما مدى جودة أدائنا في هذه الجوانب؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
),
),
const SizedBox(height: 20),
const SizedBox(height: 15),
FittedBox(
fit: BoxFit
.scaleDown, // This will make the text smaller to fit within the available space
child: Text(
context.translate('How good did we do in these aspects?',
'ما مدى جودة أدائنا في هذه الجوانب؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
),
)),
const SizedBox(height: 15),
_buildRatingRow(
context.translate('Ease of use', 'سهولة الاستخدام'),
_easeOfUseRating,
(rating) {
(rating) {
setState(() {
_easeOfUseRating = rating;
});
@ -330,7 +376,7 @@ class _FeedbackFormState extends State<FeedbackForm>
_buildRatingRow(
context.translate('Quality', 'الجودة'),
_qualityRating,
(rating) {
(rating) {
setState(() {
_qualityRating = rating;
});
@ -339,7 +385,7 @@ class _FeedbackFormState extends State<FeedbackForm>
_buildRatingRow(
context.translate('Design', 'التصميم'),
_designRating,
(rating) {
(rating) {
setState(() {
_designRating = rating;
});
@ -348,7 +394,7 @@ class _FeedbackFormState extends State<FeedbackForm>
_buildRatingRow(
context.translate('Redundant', 'مكرر'),
_redundancyRating,
(rating) {
(rating) {
setState(() {
_redundancyRating = rating;
});
@ -360,23 +406,43 @@ class _FeedbackFormState extends State<FeedbackForm>
minLines: 5, // Start with 5 lines
maxLines: null, // Allows the field to expand automatically
maxLength: _characterLimit,
style: TextStyle(color: Color(0xFF898C81)),
decoration: InputDecoration(
hintText: context.translate(
'Tell us how we can improve', 'أخبرنا كيف يمكننا التحسين'),
hintStyle: TextStyle(color: Color(0xFF898C81)),
border: OutlineInputBorder(
borderSide: BorderSide(
color: _hasError ? Colors.red : Color(0xFF7296BE),
),
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Color(0xFF7296BE), width: 1),
),
// Border when the TextField is enabled but not focused
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Color(0xFF7296BE), width: 1),
),
// Border when there is an error
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Color(0xFFD83731), width: 1),
),
// Border when focused and there is an error
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Color(0xFFD83731), width: 1),
),
errorText:
_hasError ? _errorMessage : null, // Show error below field
_hasError ? _errorMessage : null, // Show error below field
),
onChanged: (text) {
// Trigger re-validation and character limit on each change
_handleTextChange();
},
),
const SizedBox(height: 20),
const SizedBox(height: 15),
Center(
child: ElevatedButton(
onPressed: _submitFeedback,
@ -416,7 +482,10 @@ class _FeedbackFormState extends State<FeedbackForm>
),
),
6.horizontalSpace,
const Icon(Icons.chevron_right_outlined),
const Icon(
Icons.chevron_right_outlined,
color: Colors.white,
),
],
),
),
@ -425,30 +494,66 @@ class _FeedbackFormState extends State<FeedbackForm>
);
}
Widget _buildThankYouMessage() {
Widget _buildThankYouMessage(String userName) {
double screenHeight = MediaQuery.of(context).size.height;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.thumb_up, size: 80, color: Color(0xFF7DAFBC)),
SizedBox(height: screenHeight / 5),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFFF9F9F9), // Circle background color
shape: BoxShape.circle, // Make it a circle
boxShadow: [
BoxShadow(
color: Color.fromRGBO(0, 0, 0, 0.2),
blurRadius: 5,
spreadRadius: 2,
offset: Offset(0, 3), // Shadow position
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
FeedBackAssetPath.thumbfb,
width: 40,
height: 40,
)
// Icon(
// Icons.thumb,
// size: 80,
// color: Color(0xFF7DAFBC), // Icon color
// ),
// const SizedBox(height: 20),
],
),
),
// Icon(Icons.thumb_up, size: 80, color: Color(0xFF7DAFBC)),
const SizedBox(height: 20),
Text(
"Thank You",
// "Thank you",
'Thank you $userName !',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const SizedBox(height: 15),
const Text(
"For your valuable feedback, we truly appreciate your input!",
style: TextStyle(fontSize: 16),
"For your valuable feedback, we truly \n appreciate your input!",
style: TextStyle(fontSize: 16, color: Color(0xFF898C81)),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () => context.go('/myhomepage'),
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
child: const Text("Go to Home"),
),
// const SizedBox(height: 30),
// ElevatedButton(
// onPressed: () => context.go('/myhomepage'),
// //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
// child: const Text("Go to Home"),
// ),
],
),
);
@ -456,69 +561,143 @@ class _FeedbackFormState extends State<FeedbackForm>
// New widget for failure message
Widget _buildFailureMessage() {
double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.error, size: 80, color: Colors.red),
SizedBox(height: screenHeight / 5),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFFF9F9F9), // Circle background color
shape: BoxShape.circle, // Make it a circle
boxShadow: [
BoxShadow(
color: Color.fromRGBO(0, 0, 0, 0.2),
blurRadius: 5,
spreadRadius: 2,
offset: Offset(0, 3), // Shadow position
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
FeedBackAssetPath.errorfb,
width: 40,
height: 40,
),])),
// Icon(Icons.error, size: 80, color: Colors.red),
const SizedBox(height: 20),
const Text("Failed to Share Feedback",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold,
color: Color(0xFF414042)),),
const SizedBox(height: 10),
Text(AppLocalizations.of(context)!.feedback_failed_msg,
style: const TextStyle(fontSize: 16),
style: const TextStyle(fontSize: 16, color: Color(0xFF898C81)),
textAlign: TextAlign.center),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () {
setState(() {
_isFeedbackFailed = false; // Reset failure state
});
},
child: const Text("Retry"),
),
// const SizedBox(height: 30),
// ElevatedButton(
// onPressed: () {
// setState(() {
// _isFeedbackFailed = false; // Reset failure state
// });
// },
// child: const Text("Retry"),
// ),
],
),
);
}
Widget _buildEmojiButton({
required IconData icon,
// required IconData icon,
required Widget icon,
required String label,
required int index,
}) {
bool isSelected = _selectedEmojiIndex == index;
return Column(
children: [
IconButton(
icon: Icon(icon, size: 40),
color: isSelected ? Colors.green : Colors.amber,
onPressed: () {
setState(() {
_selectedEmojiIndex = index;
_isSmileySelected = true; // Hide error when a smiley is selected
});
},
),
Text(
label,
style: TextStyle(
fontWeight: FontWeight.w400,
color: isSelected ? Colors.green : Color(0xFF000000),
return GestureDetector(
onTap: () {
setState(() {
_selectedEmojiIndex = index;
_isSmileySelected = true; // Hide error when a smiley is selected
});
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
decoration: BoxDecoration(
border: Border.all(
color: isSelected ? Color(0xFF8E8E8E) : Colors.transparent,
width: 2,
),
borderRadius: BorderRadius.circular(12), // Rounded corners
),
],
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
icon, // Assuming `icon` is an Icon widget
SizedBox(height: 3),
FittedBox(
fit: BoxFit
.scaleDown, // This will make the text smaller to fit within the available space
child: Text(
label,
style: TextStyle(
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
fontSize: 14,
color: Colors.black,
// color: isSelected ? Colors.green : Colors.black,
),
),
),
],
),
),
);
// return Column(
// children: [
//
// IconButton(
// // icon: Icon(icon, size: 40),
// icon: icon,
// color: isSelected ? Colors.green : Colors.amber,
// onPressed: () {
// setState(() {
// _selectedEmojiIndex = index;
// _isSmileySelected = true; // Hide error when a smiley is selected
// });
// },
// ),
// Text(
// label,
// style: TextStyle(
// fontWeight: FontWeight.w400,
// color: isSelected ? Colors.green : Color(0xFF000000),
// ),
// ),
// ],
// );
}
Widget _buildRatingRow(
String label,
double rating,
Function(double) onRatingUpdate,
) {
String label,
double rating,
Function(double) onRatingUpdate,
) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
@ -535,10 +714,14 @@ class _FeedbackFormState extends State<FeedbackForm>
allowHalfRating: true,
itemCount: 5,
itemSize: 30,
itemBuilder: (context, _) => const Icon(
Icons.star,
itemBuilder: (context, index) => Icon(
index < rating ? Icons.star : Icons.star_border,
color: Colors.amber,
),
// itemBuilder: (context, _) => const Icon(
// Icons.star,
// color: Colors.amber,
// ),
onRatingUpdate: onRatingUpdate,
unratedColor: Color(0xFF8E8E8E),
),
@ -573,7 +756,7 @@ class _FeedbackFormState extends State<FeedbackForm>
"design": _designRating,
"redundancy": _redundancyRating,
"emoji_rating": _emojiOptions[_selectedEmojiIndex!]
["label"], // Emoji rating
["label"], // Emoji rating
"feedback": _feedbackController.text,
};
@ -783,7 +966,7 @@ class LangToggle extends ConsumerWidget {
ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
(prefs) => prefs.copyWith(language: languageAfter),
);
);
await onLoadFeedback(); // Reload feedback text after toggling language
},
@ -793,4 +976,4 @@ class LangToggle extends ConsumerWidget {
pathColorWhenOff: Colors.grey.shade300,
);
}
}
}

View File

@ -622,9 +622,10 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
double mywidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: widget.title,
backgroundColor: widget.appbarColor,
backgroundColor: Colors.white,
leadingWidth: widget.showBackButton ? mywidth * 0.25 : null,
bottom: widget.showDivider
? PreferredSize(
@ -762,13 +763,13 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
ListTile(
leading: Image.asset(
DrawerAssetIconPath.feedback,
color: Colors.black,
color: Colors.black ,
width: 20,
height: 20,
),
// title: const Text('Feedback'),
title: Text(AppLocalizations.of(context)!.feedback_title),
onTap: () => context.go('/feedback'),
onTap: () => context.go('/feedback', extra: userName),
),
if (role == 'admin')
ListTile(
@ -785,7 +786,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
onTap: () => context.go('/manageuser'),
),
ListTile(
leading: Icon(Icons.collections_bookmark_rounded),
leading: Icon(Icons.bookmarks_outlined),
title: const Text('Bookmark'),
onTap: () => context.go('/bookmark'),
),

View File

@ -1,7 +1,7 @@
name: uae_stat
description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness."
publish_to: "none"
version: 0.5.10
version: 1.0.9+10
environment:
sdk: ">=3.2.3 <4.0.0"
@ -112,6 +112,22 @@ flutter:
- assets/cached_responses/sharepoint/country_leader_imgs/
- assets/edit_profile/
- assets/splash_screen/
- assets/icons/uae_numbers/bookmarks.png
- assets/icons/uae_numbers/share.png
- assets/icons/misc/vector.png
- assets/icons/misc/visible_on.png
- assets/icons/misc/visibility_off.png
- assets/icons/misc/save.png
- assets/icons/drawer/guide.png
- assets/icons/drawer/feedback.png
- assets/icons/drawer/manageusr.png
- assets/feedback/disappointed.png
- assets/feedback/frowning.png
- assets/feedback/neutral.png
- assets/feedback/smiling.png
- assets/feedback/smilingopenface.png
- assets/feedback/errorfb.png
- assets/feedback/thumbfb.png
fonts:
- family: Segoe