bug fix
This commit is contained in:
parent
1b03125858
commit
6b5a4b96ae
@ -1035,49 +1035,47 @@
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// chart_heading :"Guest Nights by Region"
|
||||
// chart_type :"fl_stacked_bar"
|
||||
// dataset :"hotels"
|
||||
// group_by :"GUEST_REGION"
|
||||
// chart_heading :"Population Growth Over Time"
|
||||
// chart_type :"line_trend"
|
||||
// dataset :"population"
|
||||
// group_by :"GENDER"
|
||||
// is_chart :"true"
|
||||
// kpi :"hotel_occupancy_rate_chart_1"
|
||||
// main_id :"economy"
|
||||
// kpi :"population"
|
||||
// main_id :"social"
|
||||
// response:[
|
||||
// {
|
||||
// "ObsKey": {
|
||||
// "FREQ": "A",
|
||||
// "GUEST_REGION": "AF",
|
||||
// "H_INDICATOR": "GUN",
|
||||
// "H_TYPE": "_Z",
|
||||
// "MEASURE": "H",
|
||||
// "GENDER": "M",
|
||||
// "MEASURE": "POP",
|
||||
// "POP_IND": "_Z",
|
||||
// "REF_AREA": "AE",
|
||||
// "SOURCE_DETAIL": "FCSC",
|
||||
// "TIME_PERIOD": "2016",
|
||||
// "UNIT_MEASURE": "NUMBER"
|
||||
// "TIME_PERIOD": "1970",
|
||||
// "UNIT_MEASURE": "PS"
|
||||
// },
|
||||
// "ObsValue": {
|
||||
// "Value": "2659446"
|
||||
// "Value": "149195"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "ObsKey": {
|
||||
// "FREQ": "A",
|
||||
// "GUEST_REGION": "OC",
|
||||
// "H_INDICATOR": "GUN",
|
||||
// "H_TYPE": "_Z",
|
||||
// "MEASURE": "H",
|
||||
// "GENDER": "F",
|
||||
// "MEASURE": "POP",
|
||||
// "POP_IND": "_Z",
|
||||
// "REF_AREA": "AE",
|
||||
// "SOURCE_DETAIL": "FCSC",
|
||||
// "TIME_PERIOD": "2018",
|
||||
// "UNIT_MEASURE": "NUMBER"
|
||||
// "TIME_PERIOD": "1995",
|
||||
// "UNIT_MEASURE": "PS"
|
||||
// },
|
||||
// "ObsValue": {
|
||||
// "Value": "1127282"
|
||||
// "Value": "850654"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ]
|
||||
// sub_id :"tourism"
|
||||
// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_GUEST_REGION,4.3.0/...A..GUN.OTH+OC+AF+EC+AM+AC+ASC+GCC+UAE.?startPeriod=2016&dimensionAtObservation=AllDimensions"
|
||||
// sub_id :"population"
|
||||
// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_POP,2.7.0/....A..?startPeriod=1970&dimensionAtObservation=AllDimensions"
|
||||
// }
|
||||
// ]
|
||||
|
||||
@ -108,7 +108,7 @@ class ChartWidget extends StatelessWidget {
|
||||
}).toSet();
|
||||
}
|
||||
|
||||
Widget buildChart(dynamic chartData) {
|
||||
Widget buildChart(dynamic chartData, BuildContext context) {
|
||||
print('chartDataccccccc $chartData');
|
||||
// Extract group_by dynamically from the chartData
|
||||
if (chartData == null || chartData['response'] == null) {
|
||||
@ -379,7 +379,7 @@ class ChartWidget extends StatelessWidget {
|
||||
),
|
||||
gridData: FlGridData(show: false),
|
||||
borderData: FlBorderData(show: false),
|
||||
barGroups: _generateBarGroups(chartData, groupByKey),
|
||||
barGroups: _generateBarGroups(context, chartData, groupByKey),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -432,6 +432,17 @@ class ChartWidget extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
);
|
||||
case 'line_trend':
|
||||
case 'line_trend':
|
||||
return LineChart(LineChartData(
|
||||
lineTouchData: lineTouchData1(),
|
||||
gridData: gridData(),
|
||||
titlesData: titlesData1(),
|
||||
borderData: borderData(),
|
||||
lineBarsData: lineBarsData(chartData),
|
||||
minX: 1970, // Adjust based on your data range
|
||||
maxX: 2025, // Adjust based on your data range
|
||||
));
|
||||
case 'fl_multi_bar':
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
@ -477,6 +488,116 @@ class ChartWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to create LineChartBarData for Males and Females
|
||||
List<LineChartBarData> lineBarsData(dynamic chartData) {
|
||||
List<LineChartBarData> lineBars = [];
|
||||
|
||||
// Filter data for males and females
|
||||
List<Map<String, dynamic>> maleData = [];
|
||||
List<Map<String, dynamic>> femaleData = [];
|
||||
|
||||
// Separate the data based on gender
|
||||
for (var entry in chartData['response']) {
|
||||
if (entry['ObsKey']['GENDER'] == 'M') {
|
||||
maleData.add(entry);
|
||||
} else if (entry['ObsKey']['GENDER'] == 'F') {
|
||||
femaleData.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data for Male
|
||||
List<FlSpot> maleSpots = maleData.map<FlSpot>((entry) {
|
||||
double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']);
|
||||
double yValue = double.parse(entry['ObsValue']['Value']);
|
||||
return FlSpot(xValue, yValue);
|
||||
}).toList();
|
||||
|
||||
// Prepare data for Female
|
||||
List<FlSpot> femaleSpots = femaleData.map<FlSpot>((entry) {
|
||||
double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']);
|
||||
double yValue = double.parse(entry['ObsValue']['Value']);
|
||||
return FlSpot(xValue, yValue);
|
||||
}).toList();
|
||||
|
||||
// Add the Male line (Blue color)
|
||||
lineBars.add(
|
||||
LineChartBarData(
|
||||
spots: maleSpots,
|
||||
isCurved: true,
|
||||
color: Colors.blue,
|
||||
barWidth: 3,
|
||||
belowBarData: BarAreaData(show: false),
|
||||
),
|
||||
);
|
||||
|
||||
// Add the Female line (Pink color)
|
||||
lineBars.add(
|
||||
LineChartBarData(
|
||||
spots: femaleSpots,
|
||||
isCurved: true,
|
||||
color: Colors.pink,
|
||||
barWidth: 3,
|
||||
belowBarData: BarAreaData(show: false),
|
||||
),
|
||||
);
|
||||
|
||||
return lineBars;
|
||||
}
|
||||
|
||||
// Sample implementation for other chart details like titles, grid, etc.
|
||||
LineTouchData lineTouchData1() {
|
||||
return LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
// tooltipBgColor: Colors.blueAccent,
|
||||
),
|
||||
// touchCallback: (LineTouchResponse touchResponse) {},
|
||||
handleBuiltInTouches: true,
|
||||
);
|
||||
}
|
||||
|
||||
FlGridData gridData() {
|
||||
return FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
getDrawingHorizontalLine: (value) =>
|
||||
FlLine(color: Colors.grey, strokeWidth: 1),
|
||||
getDrawingVerticalLine: (value) =>
|
||||
FlLine(color: Colors.grey, strokeWidth: 1),
|
||||
);
|
||||
}
|
||||
|
||||
FlTitlesData titlesData1() {
|
||||
return FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTitlesWidget: (value, meta) {
|
||||
return Text(
|
||||
value.toInt().toString(),
|
||||
style: TextStyle(color: Colors.black, fontSize: 12),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTitlesWidget: (value, meta) {
|
||||
return Text(
|
||||
value.toInt().toString(),
|
||||
style: TextStyle(color: Colors.black, fontSize: 12),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
FlBorderData borderData() {
|
||||
return FlBorderData(
|
||||
show: true, border: Border.all(color: Colors.black, width: 1));
|
||||
}
|
||||
|
||||
// Define the list of unique colors
|
||||
final List<Color> uniqueColors = [
|
||||
Color(0xFF648CBA),
|
||||
@ -493,7 +614,7 @@ class ChartWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<BarChartGroupData> _generateBarGroups(
|
||||
dynamic chartData, String groupByKey) {
|
||||
BuildContext context, dynamic chartData, String groupByKey) {
|
||||
Map<String, Map<String, double>> groupedData = {};
|
||||
|
||||
// Group data by `group_by` and `TIME_PERIOD`, accumulating the values for each group and year
|
||||
@ -521,6 +642,8 @@ class ChartWidget extends StatelessWidget {
|
||||
List<String> timePeriods = groupedData.values.first.keys.toList();
|
||||
print('timePeriods $timePeriods');
|
||||
|
||||
double barWidth = calculateBarWidth(context, timePeriods.length);
|
||||
|
||||
// For each time period, generate a BarChartGroupData
|
||||
for (int i = 0; i < timePeriods.length; i++) {
|
||||
// Intermediate variable to track the current toY value as we stack bars
|
||||
@ -555,7 +678,8 @@ class ChartWidget extends StatelessWidget {
|
||||
BarChartRodData(
|
||||
toY: currentToY, // Use the accumulated `currentToY` value
|
||||
rodStackItems: rodStackItems, // Add the stacked items
|
||||
width: 20,
|
||||
// width: 20,
|
||||
width: barWidth,
|
||||
borderRadius: BorderRadius.zero,
|
||||
color: Colors
|
||||
.transparent, // This will act as a container for stacked items
|
||||
@ -644,10 +768,36 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return buildChart(chartData);
|
||||
return buildChart(chartData, context);
|
||||
}
|
||||
}
|
||||
|
||||
double calculateBarWidth(BuildContext context, int totalBars) {
|
||||
double width = MediaQuery.of(context).size.width; // Chart width
|
||||
double padding = 16.0; // Space between bars
|
||||
double availableWidth = (width - 100);
|
||||
if (totalBars <= 0) {
|
||||
throw ArgumentError("TotalBars must be greater than 0");
|
||||
}
|
||||
|
||||
double barWidth = (availableWidth - (padding * (totalBars - 1))) / totalBars;
|
||||
|
||||
print('barWidth - $barWidth');
|
||||
|
||||
// Ensure bar width does not exceed availableWidth
|
||||
if (barWidth > availableWidth) {
|
||||
barWidth = availableWidth;
|
||||
}
|
||||
|
||||
// Optional: Add a minimum width constraint if needed
|
||||
double minWidth = 10.0; // Example minimum width
|
||||
if (barWidth < minWidth) {
|
||||
barWidth = minWidth;
|
||||
}
|
||||
|
||||
return barWidth;
|
||||
}
|
||||
|
||||
class ChartData {
|
||||
final String? x;
|
||||
final double y;
|
||||
|
||||
@ -11,10 +11,11 @@ import 'package:intl/intl.dart';
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../../../Screens/auth_verification/changepassword.dart';
|
||||
import '../custom_drawer_routes.dart';
|
||||
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
||||
|
||||
class EditProfile extends StatefulWidget {
|
||||
const EditProfile({super.key});
|
||||
|
||||
@override
|
||||
State<EditProfile> createState() => _EditProfileState();
|
||||
}
|
||||
@ -31,7 +32,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
]; // One for each TextField
|
||||
|
||||
String _getControllerText(int index) {
|
||||
@ -57,7 +58,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
'United Arab Emirates',
|
||||
'United States',
|
||||
'India',
|
||||
'Canada'
|
||||
'Canada',
|
||||
];
|
||||
String? _selectedCountry;
|
||||
bool isChecked = false;
|
||||
@ -65,11 +66,15 @@ class _EditProfileState extends State<EditProfile> {
|
||||
final _picker = ImagePicker();
|
||||
File? _profileImage;
|
||||
String _avatarUrl = '';
|
||||
bool isPageLoad = false;
|
||||
|
||||
// Regular expression to validate Full Name (no special characters)
|
||||
final RegExp _nameRegExp = RegExp(r"^[a-zA-Z\s]+$");
|
||||
final RegExp _nameRegExp = RegExp(r'^[a-zA-Z\s]+$');
|
||||
late Future<RecordModel> userDetails;
|
||||
dynamic userId;
|
||||
dynamic recordId;
|
||||
dynamic collectionId;
|
||||
dynamic role;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -147,16 +152,22 @@ class _EditProfileState extends State<EditProfile> {
|
||||
|
||||
Future<void> _fetchUserData() async {
|
||||
try {
|
||||
setState(() {
|
||||
isPageLoad = true; // Show loader
|
||||
print('Im isPageLoad');
|
||||
});
|
||||
print('EDIT PROFILE isPageLoad');
|
||||
final adminAuth = await _pb.admins
|
||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||
final adminToken = adminAuth.token;
|
||||
print('adminToken- ${adminToken}');
|
||||
print('adminToken- $adminToken');
|
||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||
userId,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $adminToken',
|
||||
},
|
||||
);
|
||||
|
||||
print('userDetails: $userDetailsResponse');
|
||||
setState(() {
|
||||
_usernameController.text = userDetailsResponse.data['uname'] ?? '';
|
||||
@ -179,9 +190,10 @@ class _EditProfileState extends State<EditProfile> {
|
||||
|
||||
// Set the avatar URL
|
||||
String avatarFilename = userDetailsResponse.data['avatar'] ?? '';
|
||||
String recordId = userId;
|
||||
String collectionId =
|
||||
recordId = userId;
|
||||
collectionId =
|
||||
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||
role = userDetailsResponse.data['role'];
|
||||
|
||||
if (avatarFilename.isNotEmpty && recordId.isNotEmpty) {
|
||||
_avatarUrl =
|
||||
@ -189,6 +201,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
} else {
|
||||
_avatarUrl = ''; // Reset to default or empty
|
||||
}
|
||||
isPageLoad = false;
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error fetching user details: $e');
|
||||
@ -229,7 +242,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
if (fileExtension == 'jpg' ||
|
||||
fileExtension == 'jpeg' ||
|
||||
fileExtension == 'png' ||
|
||||
fileExtension == "heic") {
|
||||
fileExtension == 'heic') {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_profileImage = File(pickedFile.path);
|
||||
@ -250,13 +263,17 @@ class _EditProfileState extends State<EditProfile> {
|
||||
final DateTime today = DateTime.now();
|
||||
final DateTime initialDate = _selectedDate ??
|
||||
today.subtract(
|
||||
const Duration(days: 365 * 18)); // Default to 18 years ago
|
||||
final DateTime firstDate = today.subtract(const Duration(
|
||||
days: 365 * 100)); // Allow picking dates back to 100 years ago
|
||||
const Duration(days: 365 * 18),
|
||||
); // Default to 18 years ago
|
||||
final DateTime firstDate = today.subtract(
|
||||
const Duration(
|
||||
days: 365 * 100,
|
||||
),
|
||||
); // Allow picking dates back to 100 years ago
|
||||
final DateTime lastDate = today; // Allow picking dates up to today
|
||||
|
||||
// Updated Date format to DD/MM/YYYY
|
||||
final DateFormat _dateFormat = DateFormat('dd/MM/yyyy');
|
||||
final DateFormat dateFormat = DateFormat('dd/MM/yyyy');
|
||||
|
||||
final DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
@ -268,7 +285,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
if (pickedDate != null && pickedDate != _selectedDate) {
|
||||
setState(() {
|
||||
_selectedDate = pickedDate;
|
||||
_dateController.text = _dateFormat.format(pickedDate);
|
||||
_dateController.text = dateFormat.format(pickedDate);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -314,7 +331,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
try {
|
||||
String userID = userId;
|
||||
|
||||
print("ShowConfirmationuserID - $userID ");
|
||||
print('ShowConfirmationuserID - $userID ');
|
||||
// Retrieve data from the country/region field
|
||||
String countryRegion =
|
||||
_selectedCountry ?? ''; // Ensure the country is selected
|
||||
@ -330,10 +347,12 @@ class _EditProfileState extends State<EditProfile> {
|
||||
|
||||
// If profile image exists, add it
|
||||
if (_profileImage != null) {
|
||||
request.files.add(await http.MultipartFile.fromPath(
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'avatar',
|
||||
_profileImage!.path,
|
||||
));
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Add headers (e.g., authorization)
|
||||
@ -341,25 +360,26 @@ class _EditProfileState extends State<EditProfile> {
|
||||
|
||||
// Send the request
|
||||
final response = await request.send();
|
||||
print(response);
|
||||
|
||||
// print(response);
|
||||
print('ShowConfirmationuser response - $response ');
|
||||
// Handle response
|
||||
if (response.statusCode == 200) {
|
||||
_resetFormFields();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Profile updated successfully!")),
|
||||
SnackBar(content: Text('Profile updated successfully!')),
|
||||
);
|
||||
// print('ShowConfirmation userData - $userData ');
|
||||
context.go('/myhomepage');
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text("Failed to update profile: ${response.statusCode}")),
|
||||
content: Text('Failed to update profile: ${response.statusCode}'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Failed to update profile: $error")),
|
||||
SnackBar(content: Text('Failed to update profile: $error')),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -392,9 +412,9 @@ class _EditProfileState extends State<EditProfile> {
|
||||
try {
|
||||
// Simulate a network delay of 2 seconds (this mimics loading an image from the internet)
|
||||
// await Future.delayed(Duration(seconds: 3));
|
||||
print("Image loaded from URL: $url");
|
||||
print('Image loaded from URL: $url');
|
||||
} catch (e) {
|
||||
print("Error loading image: $e");
|
||||
print('Error loading image: $e');
|
||||
throw Exception('Failed to load image');
|
||||
}
|
||||
}
|
||||
@ -413,7 +433,11 @@ class _EditProfileState extends State<EditProfile> {
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
child: isPageLoad
|
||||
? Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
// CircleAvatar(
|
||||
// radius: 50,
|
||||
@ -446,6 +470,52 @@ class _EditProfileState extends State<EditProfile> {
|
||||
// ),
|
||||
// ),
|
||||
|
||||
// Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// // FutureBuilder to load the image
|
||||
// FutureBuilder(
|
||||
// future: _loadProfileImage(),
|
||||
// builder: (context, snapshot) {
|
||||
// if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
// // While the image is loading, show a progress indicator
|
||||
// return CircleAvatar(
|
||||
// radius: 50,
|
||||
// child: CircularProgressIndicator(),
|
||||
// );
|
||||
// } else if (snapshot.hasError || snapshot.data == null) {
|
||||
// // If there's an error or no image, show an error icon
|
||||
// return CircleAvatar(
|
||||
// radius: 50,
|
||||
// child: CircularProgressIndicator(),
|
||||
// );
|
||||
// } else {
|
||||
// // Display the loaded image
|
||||
// return CircleAvatar(
|
||||
// radius: 50,
|
||||
// backgroundImage: snapshot.data,
|
||||
// child: Align(
|
||||
// alignment: Alignment.bottomRight,
|
||||
// child: GestureDetector(
|
||||
// onTap: _pickImage,
|
||||
// child: CircleAvatar(
|
||||
// radius: 15,
|
||||
// backgroundColor: Colors.white,
|
||||
// child: Icon(
|
||||
// Icons.camera_alt,
|
||||
// size: 15,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
@ -458,7 +528,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
? NetworkImage(
|
||||
_avatarUrl) // Load from URL
|
||||
: AssetImage(
|
||||
"assets/edit_profile/profile.png")
|
||||
'assets/edit_profile/profile.png')
|
||||
as ImageProvider,
|
||||
child: _avatarUrl.isNotEmpty
|
||||
? FutureBuilder(
|
||||
@ -467,7 +537,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child:
|
||||
CircularProgressIndicator(),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(
|
||||
@ -475,13 +546,15 @@ class _EditProfileState extends State<EditProfile> {
|
||||
);
|
||||
} else {
|
||||
return Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
alignment:
|
||||
Alignment.bottomRight,
|
||||
child: GestureDetector(
|
||||
onTap:
|
||||
_pickImage, // Call `_pickImage` on tap
|
||||
child: CircleAvatar(
|
||||
radius: 15,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor:
|
||||
Colors.white,
|
||||
child: Icon(
|
||||
Icons.camera_alt,
|
||||
size: 15,
|
||||
@ -514,8 +587,10 @@ class _EditProfileState extends State<EditProfile> {
|
||||
Positioned(
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Colors.grey),
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(
|
||||
Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -530,7 +605,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey),
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -556,7 +632,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey),
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -581,7 +658,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey),
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -595,7 +673,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
|
||||
//RegExp(r"^[a-zA-Z\s]+$");
|
||||
final nameRegex = RegExp(
|
||||
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$");
|
||||
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$",
|
||||
);
|
||||
if (!nameRegex.hasMatch(value)) {
|
||||
return 'Invalid Characters';
|
||||
}
|
||||
@ -614,18 +693,20 @@ class _EditProfileState extends State<EditProfile> {
|
||||
),
|
||||
maxLength:
|
||||
40, // Set the maximum length to 20 characters
|
||||
maxLengthEnforcement: MaxLengthEnforcement.enforced,
|
||||
maxLengthEnforcement:
|
||||
MaxLengthEnforcement.enforced,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Date of Birth*",
|
||||
'Date of Birth*',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey),
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -639,7 +720,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
),
|
||||
// hintText: _showHints[3] ? 'Select your Date of Birth' : null,
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
suffixIcon: const Icon(Icons.arrow_drop_down_sharp),
|
||||
suffixIcon:
|
||||
const Icon(Icons.arrow_drop_down_sharp),
|
||||
enabled: !_isProfileCompleted,
|
||||
),
|
||||
readOnly: true,
|
||||
@ -653,7 +735,9 @@ class _EditProfileState extends State<EditProfile> {
|
||||
Text(
|
||||
AppLocalizations.of(context)!.region,
|
||||
style: TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.w500),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -667,10 +751,12 @@ class _EditProfileState extends State<EditProfile> {
|
||||
labelText: 'Select',
|
||||
),
|
||||
items: _countries
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
.map(
|
||||
(item) => DropdownMenuItem<String>(
|
||||
value: item,
|
||||
child: Text(item),
|
||||
))
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
@ -695,8 +781,9 @@ class _EditProfileState extends State<EditProfile> {
|
||||
});
|
||||
},
|
||||
side: BorderSide(
|
||||
color:
|
||||
showError ? Colors.red : Colors.grey,
|
||||
color: showError
|
||||
? Colors.red
|
||||
: Colors.grey,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
@ -710,19 +797,22 @@ class _EditProfileState extends State<EditProfile> {
|
||||
SizedBox(height: 10),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)!
|
||||
text: AppLocalizations.of(
|
||||
context)!
|
||||
.agree,
|
||||
style:
|
||||
TextStyle(color: Colors.black),
|
||||
style: TextStyle(
|
||||
color: Colors.black),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(
|
||||
context)!
|
||||
context,
|
||||
)!
|
||||
.terms_conditions,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration:
|
||||
TextDecoration.underline,
|
||||
TextDecoration
|
||||
.underline,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
@ -732,19 +822,23 @@ class _EditProfileState extends State<EditProfile> {
|
||||
),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(
|
||||
context)!
|
||||
context,
|
||||
)!
|
||||
.t_and,
|
||||
style: TextStyle(
|
||||
color: Colors.black),
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(
|
||||
context)!
|
||||
context,
|
||||
)!
|
||||
.privacy_policy,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration:
|
||||
TextDecoration.underline,
|
||||
TextDecoration
|
||||
.underline,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
@ -754,10 +848,12 @@ class _EditProfileState extends State<EditProfile> {
|
||||
),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(
|
||||
context)!
|
||||
context,
|
||||
)!
|
||||
.conditions,
|
||||
style: TextStyle(
|
||||
color: Colors.black),
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -773,11 +869,12 @@ class _EditProfileState extends State<EditProfile> {
|
||||
),
|
||||
if (showError)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 10.0),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10.0),
|
||||
child: Text(
|
||||
'Please agree to terms and conditions',
|
||||
style: TextStyle(
|
||||
@ -798,10 +895,12 @@ class _EditProfileState extends State<EditProfile> {
|
||||
context.go('/createNewPw/$userId/$email');
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.change_password,
|
||||
AppLocalizations.of(context)!
|
||||
.change_password,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline),
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -841,7 +940,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
}
|
||||
|
||||
class ConfirmationDialog extends StatelessWidget {
|
||||
const ConfirmationDialog({Key? key}) : super(key: key);
|
||||
const ConfirmationDialog({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
@ -849,7 +948,7 @@ class ConfirmationDialog extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Are you sure you want to save this page?",
|
||||
'Are you sure you want to save this page?',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF898C81),
|
||||
@ -861,7 +960,7 @@ class ConfirmationDialog extends StatelessWidget {
|
||||
SizedBox(height: 8),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
text: "Once saved, you will not be able to change your ",
|
||||
text: 'Once saved, you will not be able to change your ',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF898C81),
|
||||
@ -870,25 +969,26 @@ class ConfirmationDialog extends StatelessWidget {
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Country",
|
||||
text: 'Country',
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.w700), // Bold for "name"
|
||||
),
|
||||
TextSpan(
|
||||
text: " or ",
|
||||
text: ' or ',
|
||||
),
|
||||
TextSpan(
|
||||
text: "Profile Image",
|
||||
text: 'Profile Image',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700), // Bold for "date of birth"
|
||||
fontWeight: FontWeight.w700,
|
||||
), // Bold for "date of birth"
|
||||
),
|
||||
TextSpan(
|
||||
text: ".",
|
||||
text: '.',
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -913,7 +1013,7 @@ class ConfirmationDialog extends StatelessWidget {
|
||||
), // Set the border color here
|
||||
),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
'Cancel',
|
||||
style: TextStyle(color: Color(0xFF92722A)),
|
||||
),
|
||||
),
|
||||
@ -938,7 +1038,7 @@ class ConfirmationDialog extends StatelessWidget {
|
||||
),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -213,7 +213,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
||||
title: Text(AppLocalizations.of(context)!.feedback_title),
|
||||
// appBar: AppBar(
|
||||
// backgroundColor: const Color(0xFFf8f9ff),
|
||||
// title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')),
|
||||
// title: Text(AppLocalizations.of(context)!.feedback_title),
|
||||
// actions: [
|
||||
// Align(
|
||||
// alignment: AlignmentDirectional.topEnd,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@ -7,19 +9,12 @@ import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../../components/indicators/locale_provider.dart';
|
||||
import '../../components/my_toggle.dart';
|
||||
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
||||
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
||||
|
||||
class BaseScaffold extends ConsumerStatefulWidget {
|
||||
final Widget body;
|
||||
final Widget title;
|
||||
final bool? showBackButton;
|
||||
final bool? mycenterTitle;
|
||||
final List<Widget>? actions;
|
||||
final Color? appbarColor, mytitleColor;
|
||||
|
||||
const BaseScaffold({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.body,
|
||||
required this.title,
|
||||
this.actions,
|
||||
@ -27,7 +22,13 @@ class BaseScaffold extends ConsumerStatefulWidget {
|
||||
this.mycenterTitle = false,
|
||||
this.appbarColor,
|
||||
this.mytitleColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
final Widget body;
|
||||
final Widget title;
|
||||
final bool? showBackButton;
|
||||
final bool? mycenterTitle;
|
||||
final List<Widget>? actions;
|
||||
final Color? appbarColor, mytitleColor;
|
||||
|
||||
@override
|
||||
_BaseScaffoldState createState() => _BaseScaffoldState();
|
||||
@ -48,6 +49,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
String? collectionIds;
|
||||
|
||||
String? collectionId;
|
||||
bool isLoggedOut = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -55,7 +57,6 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
_checkUserId();
|
||||
}
|
||||
|
||||
// Method to retrieve userId from SharedPreferences
|
||||
Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('userId'); // Retrieve the userId
|
||||
@ -69,125 +70,39 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
userId = fetchedUserId;
|
||||
});
|
||||
//print('NAVUser ID: $userId');
|
||||
_initializeUserData();
|
||||
_fetchUserData();
|
||||
} else {
|
||||
print('No userId found');
|
||||
// Handle the case where userId is not available
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> _fetchUserData() async {
|
||||
// try {
|
||||
// final adminAuth = await _pb.admins
|
||||
// .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||
// final adminToken = adminAuth.token;
|
||||
// //print('adminToken- ${adminToken}');
|
||||
// final userDetailsResponse = await _pb.collection('users').getOne(
|
||||
// userId!,
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer $adminToken',
|
||||
// },
|
||||
// );
|
||||
// print('NAVuserDetails: $userDetailsResponse');
|
||||
//
|
||||
// setState(() {
|
||||
// userName = userDetailsResponse.data['uname'] ?? '';
|
||||
// userEmail = userDetailsResponse.data['email'] ?? '';
|
||||
// userAvatar = userDetailsResponse.data['avatar'] ?? '';
|
||||
// role = userDetailsResponse.data['role'] ?? '';
|
||||
// String recordId = userId;
|
||||
// String collectionId =
|
||||
// userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||
//
|
||||
// if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
|
||||
// _avatarUrl =
|
||||
// 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar';
|
||||
// } else {
|
||||
// _avatarUrl = ''; // Reset to default or empty
|
||||
// }
|
||||
// });
|
||||
// } catch (e) {
|
||||
// print('Error fetching user details: $e');
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _initializeUserData() async {
|
||||
print("Laoding 1");
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
// final userDataString = prefs.getString('userData');
|
||||
|
||||
if (prefs.containsKey('userData')) {
|
||||
// print("Loading user data from local storage: $userData");
|
||||
final String? userDataString = prefs.getString('userData');
|
||||
|
||||
if (userDataString != null) {
|
||||
// Decode the JSON string into a Map
|
||||
final Map<String, dynamic> userData = jsonDecode(userDataString);
|
||||
|
||||
// Access the userName field
|
||||
setState(() {
|
||||
userName = userData['userNames'];
|
||||
userEmail = userData['userEmails'];
|
||||
userAvatar = userData['userAvatars'];
|
||||
role = userData['roles'];
|
||||
String recordId = userId;
|
||||
String collectionId = userData['collectionIds'];
|
||||
|
||||
if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
|
||||
_avatarUrl =
|
||||
'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar';
|
||||
} else {
|
||||
_avatarUrl = ''; // Reset to default or empty
|
||||
}
|
||||
});
|
||||
} else {
|
||||
print("No user data found in local storage.");
|
||||
}
|
||||
} else {
|
||||
// Fetch data from server and save it to local storage
|
||||
|
||||
await _fetchUserData();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchUserData() async {
|
||||
try {
|
||||
// print("Fetching _fetchUserData");
|
||||
final adminAuth = await _pb.admins
|
||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||
final adminToken = adminAuth.token;
|
||||
|
||||
//print('adminToken- ${adminToken}');
|
||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||
userId!,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $adminToken',
|
||||
},
|
||||
);
|
||||
// print('User details fetched from server: $userDetailsResponse');
|
||||
|
||||
final userData = {
|
||||
'userNames': userDetailsResponse.data['uname'] ?? '',
|
||||
'userEmails': userDetailsResponse.data['email'] ?? '',
|
||||
'userAvatars': userDetailsResponse.data['avatar'] ?? '',
|
||||
'roles': userDetailsResponse.data['role'] ?? '',
|
||||
'collectionIds':
|
||||
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_',
|
||||
};
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('userData', jsonEncode(userData));
|
||||
print('NAVuserDetails: $userDetailsResponse');
|
||||
|
||||
setState(() {
|
||||
userNames = userData['userNames'];
|
||||
userEmails = userData['userEmails'];
|
||||
userAvatars = userData['userAvatars'];
|
||||
roles = userData['roles'];
|
||||
userName = userDetailsResponse.data['uname'] ?? '';
|
||||
userEmail = userDetailsResponse.data['email'] ?? '';
|
||||
userAvatar = userDetailsResponse.data['avatar'] ?? '';
|
||||
role = userDetailsResponse.data['role'] ?? '';
|
||||
String recordId = userId;
|
||||
collectionIds = userData['collectionIds'];
|
||||
String collectionId =
|
||||
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||
|
||||
if (userAvatars!.isNotEmpty && recordId.isNotEmpty) {
|
||||
if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
|
||||
_avatarUrl =
|
||||
'https://pb.venbait.in/api/files/$collectionIds/$recordId/$userAvatars';
|
||||
'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar';
|
||||
} else {
|
||||
_avatarUrl = ''; // Reset to default or empty
|
||||
}
|
||||
@ -199,6 +114,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
|
||||
Future<void> logout() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
// setState(() {
|
||||
// isLoggedOut = true;
|
||||
// });
|
||||
prefs.clear();
|
||||
context.go('/');
|
||||
}
|
||||
@ -221,14 +139,16 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
// onPressed: () {},
|
||||
// icon: const Icon(Icons.toggle_off_outlined),
|
||||
// ),
|
||||
MyToggle(isOn: locale?.languageCode == 'en',
|
||||
MyToggle(
|
||||
isOn: locale?.languageCode == 'en',
|
||||
knobTextWhenOn: 'ع',
|
||||
knobTextWhenOff: 'EN',
|
||||
pathColorWhenOn: Colors.grey.shade300,
|
||||
pathColorWhenOff: Colors.grey.shade300,
|
||||
onTap: (){
|
||||
onTap: () {
|
||||
ref.read(localeProvider.notifier).toggleLocale();
|
||||
},),
|
||||
},
|
||||
),
|
||||
],
|
||||
leading: Stack(
|
||||
children: [
|
||||
@ -274,7 +194,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
SizedBox(
|
||||
width: mywidth / 8,
|
||||
child: Image(
|
||||
image: AssetImage('assets/logos/fcsc.png'))),
|
||||
image: AssetImage('assets/logos/fcsc.png'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(),
|
||||
@ -295,7 +217,8 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
)
|
||||
: Image(
|
||||
image: AssetImage(
|
||||
'assets/edit_profile/profile.png'),
|
||||
'assets/edit_profile/profile.png',
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
@ -317,10 +240,10 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -332,8 +255,10 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
if (role == 'admin')
|
||||
ListTile(
|
||||
leading: Icon(Icons.manage_accounts),
|
||||
// title: Text('Manage User'),
|
||||
title: Text(AppLocalizations.of(context)!.manage_user,),
|
||||
title: Text('Manage User'),
|
||||
// title: Text(
|
||||
// AppLocalizations.of(context)!.manage_user,
|
||||
// ),
|
||||
onTap: () => context.go('/manageuser'),
|
||||
),
|
||||
ListTile(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user