app code add
This commit is contained in:
commit
920a1d3aa9
@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||||
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
|
<option name="filePath" value="$PROJECT_DIR$/frontend/lib/main.dart" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@ -29,8 +29,12 @@ android {
|
|||||||
// ndkVersion = flutter.ndkVersion
|
// ndkVersion = flutter.ndkVersion
|
||||||
ndkVersion = "25.1.8937393"
|
ndkVersion = "25.1.8937393"
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_21
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_21
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
org.gradle.java.home=C:/Program Files/Eclipse Adoptium/jdk-21.0.5.11-hotspot
|
org.gradleorg.gradle.java.home=/usr/lib/jvm/openlogic-openjdk-17.0.13+11-linux-x64
|
||||||
|
|||||||
@ -1035,49 +1035,47 @@
|
|||||||
//
|
//
|
||||||
// [
|
// [
|
||||||
// {
|
// {
|
||||||
// chart_heading :"Guest Nights by Region"
|
// chart_heading :"Population Growth Over Time"
|
||||||
// chart_type :"fl_stacked_bar"
|
// chart_type :"line_trend"
|
||||||
// dataset :"hotels"
|
// dataset :"population"
|
||||||
// group_by :"GUEST_REGION"
|
// group_by :"GENDER"
|
||||||
// is_chart :"true"
|
// is_chart :"true"
|
||||||
// kpi :"hotel_occupancy_rate_chart_1"
|
// kpi :"population"
|
||||||
// main_id :"economy"
|
// main_id :"social"
|
||||||
// response:[
|
// response:[
|
||||||
// {
|
// {
|
||||||
// "ObsKey": {
|
// "ObsKey": {
|
||||||
// "FREQ": "A",
|
// "FREQ": "A",
|
||||||
// "GUEST_REGION": "AF",
|
// "GENDER": "M",
|
||||||
// "H_INDICATOR": "GUN",
|
// "MEASURE": "POP",
|
||||||
// "H_TYPE": "_Z",
|
// "POP_IND": "_Z",
|
||||||
// "MEASURE": "H",
|
|
||||||
// "REF_AREA": "AE",
|
// "REF_AREA": "AE",
|
||||||
// "SOURCE_DETAIL": "FCSC",
|
// "SOURCE_DETAIL": "FCSC",
|
||||||
// "TIME_PERIOD": "2016",
|
// "TIME_PERIOD": "1970",
|
||||||
// "UNIT_MEASURE": "NUMBER"
|
// "UNIT_MEASURE": "PS"
|
||||||
// },
|
// },
|
||||||
// "ObsValue": {
|
// "ObsValue": {
|
||||||
// "Value": "2659446"
|
// "Value": "149195"
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
// },
|
|
||||||
// {
|
// {
|
||||||
// "ObsKey": {
|
// "ObsKey": {
|
||||||
// "FREQ": "A",
|
// "FREQ": "A",
|
||||||
// "GUEST_REGION": "OC",
|
// "GENDER": "F",
|
||||||
// "H_INDICATOR": "GUN",
|
// "MEASURE": "POP",
|
||||||
// "H_TYPE": "_Z",
|
// "POP_IND": "_Z",
|
||||||
// "MEASURE": "H",
|
|
||||||
// "REF_AREA": "AE",
|
// "REF_AREA": "AE",
|
||||||
// "SOURCE_DETAIL": "FCSC",
|
// "SOURCE_DETAIL": "FCSC",
|
||||||
// "TIME_PERIOD": "2018",
|
// "TIME_PERIOD": "1995",
|
||||||
// "UNIT_MEASURE": "NUMBER"
|
// "UNIT_MEASURE": "PS"
|
||||||
// },
|
// },
|
||||||
// "ObsValue": {
|
// "ObsValue": {
|
||||||
// "Value": "1127282"
|
// "Value": "850654"
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// ]
|
// ]
|
||||||
// sub_id :"tourism"
|
// sub_id :"population"
|
||||||
// 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"
|
// 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();
|
}).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildChart(dynamic chartData) {
|
Widget buildChart(dynamic chartData, BuildContext context) {
|
||||||
print('chartDataccccccc $chartData');
|
print('chartDataccccccc $chartData');
|
||||||
// Extract group_by dynamically from the chartData
|
// Extract group_by dynamically from the chartData
|
||||||
if (chartData == null || chartData['response'] == null) {
|
if (chartData == null || chartData['response'] == null) {
|
||||||
@ -379,7 +379,7 @@ class ChartWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
gridData: FlGridData(show: false),
|
gridData: FlGridData(show: false),
|
||||||
borderData: FlBorderData(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':
|
case 'fl_multi_bar':
|
||||||
return BarChart(
|
return BarChart(
|
||||||
BarChartData(
|
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
|
// Define the list of unique colors
|
||||||
final List<Color> uniqueColors = [
|
final List<Color> uniqueColors = [
|
||||||
Color(0xFF648CBA),
|
Color(0xFF648CBA),
|
||||||
@ -493,7 +614,7 @@ class ChartWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<BarChartGroupData> _generateBarGroups(
|
List<BarChartGroupData> _generateBarGroups(
|
||||||
dynamic chartData, String groupByKey) {
|
BuildContext context, dynamic chartData, String groupByKey) {
|
||||||
Map<String, Map<String, double>> groupedData = {};
|
Map<String, Map<String, double>> groupedData = {};
|
||||||
|
|
||||||
// Group data by `group_by` and `TIME_PERIOD`, accumulating the values for each group and year
|
// 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();
|
List<String> timePeriods = groupedData.values.first.keys.toList();
|
||||||
print('timePeriods $timePeriods');
|
print('timePeriods $timePeriods');
|
||||||
|
|
||||||
|
double barWidth = calculateBarWidth(context, timePeriods.length);
|
||||||
|
|
||||||
// For each time period, generate a BarChartGroupData
|
// For each time period, generate a BarChartGroupData
|
||||||
for (int i = 0; i < timePeriods.length; i++) {
|
for (int i = 0; i < timePeriods.length; i++) {
|
||||||
// Intermediate variable to track the current toY value as we stack bars
|
// Intermediate variable to track the current toY value as we stack bars
|
||||||
@ -555,7 +678,8 @@ class ChartWidget extends StatelessWidget {
|
|||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
toY: currentToY, // Use the accumulated `currentToY` value
|
toY: currentToY, // Use the accumulated `currentToY` value
|
||||||
rodStackItems: rodStackItems, // Add the stacked items
|
rodStackItems: rodStackItems, // Add the stacked items
|
||||||
width: 20,
|
// width: 20,
|
||||||
|
width: barWidth,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.zero,
|
||||||
color: Colors
|
color: Colors
|
||||||
.transparent, // This will act as a container for stacked items
|
.transparent, // This will act as a container for stacked items
|
||||||
@ -644,10 +768,36 @@ class ChartWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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 {
|
class ChartData {
|
||||||
final String? x;
|
final String? x;
|
||||||
final double y;
|
final double y;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -11,10 +12,11 @@ import 'package:intl/intl.dart';
|
|||||||
import 'package:pocketbase/pocketbase.dart';
|
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 '../../../Screens/auth_verification/changepassword.dart';
|
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
||||||
import '../custom_drawer_routes.dart';
|
|
||||||
|
|
||||||
class EditProfile extends StatefulWidget {
|
class EditProfile extends StatefulWidget {
|
||||||
|
const EditProfile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EditProfile> createState() => _EditProfileState();
|
State<EditProfile> createState() => _EditProfileState();
|
||||||
}
|
}
|
||||||
@ -31,7 +33,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true
|
true,
|
||||||
]; // One for each TextField
|
]; // One for each TextField
|
||||||
|
|
||||||
String _getControllerText(int index) {
|
String _getControllerText(int index) {
|
||||||
@ -57,7 +59,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
'United Arab Emirates',
|
'United Arab Emirates',
|
||||||
'United States',
|
'United States',
|
||||||
'India',
|
'India',
|
||||||
'Canada'
|
'Canada',
|
||||||
];
|
];
|
||||||
String? _selectedCountry;
|
String? _selectedCountry;
|
||||||
bool isChecked = false;
|
bool isChecked = false;
|
||||||
@ -65,11 +67,15 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
final _picker = ImagePicker();
|
final _picker = ImagePicker();
|
||||||
File? _profileImage;
|
File? _profileImage;
|
||||||
String _avatarUrl = '';
|
String _avatarUrl = '';
|
||||||
|
bool isPageLoad= false;
|
||||||
|
|
||||||
// Regular expression to validate Full Name (no special characters)
|
// 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;
|
late Future<RecordModel> userDetails;
|
||||||
dynamic userId;
|
dynamic userId;
|
||||||
|
dynamic recordId;
|
||||||
|
dynamic collectionId;
|
||||||
|
dynamic role;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -147,16 +153,23 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
|
|
||||||
Future<void> _fetchUserData() async {
|
Future<void> _fetchUserData() async {
|
||||||
try {
|
try {
|
||||||
|
setState(() {
|
||||||
|
isPageLoad = true; // Show loader
|
||||||
|
print('Im isPageLoad');
|
||||||
|
});
|
||||||
|
print('EDIT PROFILE isPageLoad');
|
||||||
final adminAuth = await _pb.admins
|
final adminAuth = await _pb.admins
|
||||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||||
final adminToken = adminAuth.token;
|
final adminToken = adminAuth.token;
|
||||||
print('adminToken- ${adminToken}');
|
print('adminToken- $adminToken');
|
||||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||||
userId,
|
userId,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer $adminToken',
|
'Authorization': 'Bearer $adminToken',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
print('userDetails: $userDetailsResponse');
|
print('userDetails: $userDetailsResponse');
|
||||||
setState(() {
|
setState(() {
|
||||||
_usernameController.text = userDetailsResponse.data['uname'] ?? '';
|
_usernameController.text = userDetailsResponse.data['uname'] ?? '';
|
||||||
@ -179,9 +192,10 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
|
|
||||||
// Set the avatar URL
|
// Set the avatar URL
|
||||||
String avatarFilename = userDetailsResponse.data['avatar'] ?? '';
|
String avatarFilename = userDetailsResponse.data['avatar'] ?? '';
|
||||||
String recordId = userId;
|
recordId = userId;
|
||||||
String collectionId =
|
collectionId =
|
||||||
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||||
|
role = userDetailsResponse.data['role'];
|
||||||
|
|
||||||
if (avatarFilename.isNotEmpty && recordId.isNotEmpty) {
|
if (avatarFilename.isNotEmpty && recordId.isNotEmpty) {
|
||||||
_avatarUrl =
|
_avatarUrl =
|
||||||
@ -189,6 +203,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
} else {
|
} else {
|
||||||
_avatarUrl = ''; // Reset to default or empty
|
_avatarUrl = ''; // Reset to default or empty
|
||||||
}
|
}
|
||||||
|
isPageLoad = false;
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching user details: $e');
|
print('Error fetching user details: $e');
|
||||||
@ -229,7 +244,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
if (fileExtension == 'jpg' ||
|
if (fileExtension == 'jpg' ||
|
||||||
fileExtension == 'jpeg' ||
|
fileExtension == 'jpeg' ||
|
||||||
fileExtension == 'png' ||
|
fileExtension == 'png' ||
|
||||||
fileExtension == "heic") {
|
fileExtension == 'heic') {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_profileImage = File(pickedFile.path);
|
_profileImage = File(pickedFile.path);
|
||||||
@ -250,13 +265,13 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
final DateTime today = DateTime.now();
|
final DateTime today = DateTime.now();
|
||||||
final DateTime initialDate = _selectedDate ??
|
final DateTime initialDate = _selectedDate ??
|
||||||
today.subtract(
|
today.subtract(
|
||||||
const Duration(days: 365 * 18)); // Default to 18 years ago
|
const Duration(days: 365 * 18),); // Default to 18 years ago
|
||||||
final DateTime firstDate = today.subtract(const Duration(
|
final DateTime firstDate = today.subtract(const Duration(
|
||||||
days: 365 * 100)); // Allow picking dates back to 100 years ago
|
days: 365 * 100,),); // Allow picking dates back to 100 years ago
|
||||||
final DateTime lastDate = today; // Allow picking dates up to today
|
final DateTime lastDate = today; // Allow picking dates up to today
|
||||||
|
|
||||||
// Updated Date format to DD/MM/YYYY
|
// 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(
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
@ -268,7 +283,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
if (pickedDate != null && pickedDate != _selectedDate) {
|
if (pickedDate != null && pickedDate != _selectedDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedDate = pickedDate;
|
_selectedDate = pickedDate;
|
||||||
_dateController.text = _dateFormat.format(pickedDate);
|
_dateController.text = dateFormat.format(pickedDate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,7 +329,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
try {
|
try {
|
||||||
String userID = userId;
|
String userID = userId;
|
||||||
|
|
||||||
print("ShowConfirmationuserID - $userID ");
|
print('ShowConfirmationuserID - $userID ');
|
||||||
// Retrieve data from the country/region field
|
// Retrieve data from the country/region field
|
||||||
String countryRegion =
|
String countryRegion =
|
||||||
_selectedCountry ?? ''; // Ensure the country is selected
|
_selectedCountry ?? ''; // Ensure the country is selected
|
||||||
@ -325,15 +340,15 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
final request = http.MultipartRequest('PATCH', uri);
|
final request = http.MultipartRequest('PATCH', uri);
|
||||||
|
|
||||||
// Add fields to the request
|
// Add fields to the request
|
||||||
request.fields['country_region'] =
|
request.fields['country_region'] = countryRegion; // Only update country here
|
||||||
countryRegion; // Only update country here
|
|
||||||
|
|
||||||
// If profile image exists, add it
|
// If profile image exists, add it
|
||||||
if (_profileImage != null) {
|
if (_profileImage != null) {
|
||||||
request.files.add(await http.MultipartFile.fromPath(
|
request.files.add(await http.MultipartFile.fromPath(
|
||||||
'avatar',
|
'avatar',
|
||||||
_profileImage!.path,
|
_profileImage!.path,
|
||||||
));
|
),);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add headers (e.g., authorization)
|
// Add headers (e.g., authorization)
|
||||||
@ -341,25 +356,27 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
|
|
||||||
// Send the request
|
// Send the request
|
||||||
final response = await request.send();
|
final response = await request.send();
|
||||||
print(response);
|
// print(response);
|
||||||
|
print('ShowConfirmationuser response - $response ');
|
||||||
// Handle response
|
// Handle response
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
|
||||||
_resetFormFields();
|
_resetFormFields();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Profile updated successfully!")),
|
SnackBar(content: Text('Profile updated successfully!')),
|
||||||
);
|
);
|
||||||
|
// print('ShowConfirmation userData - $userData ');
|
||||||
context.go('/myhomepage');
|
context.go('/myhomepage');
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content:
|
content:
|
||||||
Text("Failed to update profile: ${response.statusCode}")),
|
Text('Failed to update profile: ${response.statusCode}'),),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Failed to update profile: $error")),
|
SnackBar(content: Text('Failed to update profile: $error')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -392,9 +409,9 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
try {
|
try {
|
||||||
// Simulate a network delay of 2 seconds (this mimics loading an image from the internet)
|
// Simulate a network delay of 2 seconds (this mimics loading an image from the internet)
|
||||||
// await Future.delayed(Duration(seconds: 3));
|
// await Future.delayed(Duration(seconds: 3));
|
||||||
print("Image loaded from URL: $url");
|
print('Image loaded from URL: $url');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Error loading image: $e");
|
print('Error loading image: $e');
|
||||||
throw Exception('Failed to load image');
|
throw Exception('Failed to load image');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +430,11 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: Column(
|
child: isPageLoad
|
||||||
|
? Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
// CircleAvatar(
|
// CircleAvatar(
|
||||||
// radius: 50,
|
// radius: 50,
|
||||||
@ -446,26 +467,69 @@ 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(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 50,
|
radius: 50,
|
||||||
backgroundImage: _profileImage != null
|
backgroundImage: _profileImage != null
|
||||||
? FileImage(
|
? FileImage(_profileImage!) // If a local file is selected
|
||||||
_profileImage!) // If a local file is selected
|
|
||||||
: _avatarUrl.isNotEmpty
|
: _avatarUrl.isNotEmpty
|
||||||
? NetworkImage(
|
? NetworkImage(_avatarUrl) // Load from URL
|
||||||
_avatarUrl) // Load from URL
|
: AssetImage('assets/edit_profile/profile.png') as ImageProvider,
|
||||||
: AssetImage(
|
|
||||||
"assets/edit_profile/profile.png")
|
|
||||||
as ImageProvider,
|
|
||||||
child: _avatarUrl.isNotEmpty
|
child: _avatarUrl.isNotEmpty
|
||||||
? FutureBuilder(
|
? FutureBuilder(
|
||||||
future: _loadImage(_avatarUrl),
|
future: _loadImage(_avatarUrl),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState ==
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
ConnectionState.waiting) {
|
|
||||||
return Center(
|
return Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
@ -477,8 +541,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap:
|
onTap: _pickImage, // Call `_pickImage` on tap
|
||||||
_pickImage, // Call `_pickImage` on tap
|
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 15,
|
radius: 15,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
@ -496,8 +559,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
: Align(
|
: Align(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap:
|
onTap: _pickImage, // Call `_pickImage` on tap
|
||||||
_pickImage, // Call `_pickImage` on tap
|
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 15,
|
radius: 15,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
@ -515,7 +577,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
Colors.grey),
|
Colors.grey,),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -530,7 +592,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.grey),
|
color: Colors.grey,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -556,7 +618,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.grey),
|
color: Colors.grey,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -581,7 +643,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.grey),
|
color: Colors.grey,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -595,7 +657,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
|
|
||||||
//RegExp(r"^[a-zA-Z\s]+$");
|
//RegExp(r"^[a-zA-Z\s]+$");
|
||||||
final nameRegex = RegExp(
|
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)) {
|
if (!nameRegex.hasMatch(value)) {
|
||||||
return 'Invalid Characters';
|
return 'Invalid Characters';
|
||||||
}
|
}
|
||||||
@ -621,12 +683,11 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
// "Date of Birth*",
|
'Date of Birth*',
|
||||||
AppLocalizations.of(context)!.dob,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.grey),
|
color: Colors.grey,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -654,7 +715,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!.region,
|
AppLocalizations.of(context)!.region,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16, fontWeight: FontWeight.w500),
|
fontSize: 16, fontWeight: FontWeight.w500,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -671,7 +732,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
.map((item) => DropdownMenuItem<String>(
|
.map((item) => DropdownMenuItem<String>(
|
||||||
value: item,
|
value: item,
|
||||||
child: Text(item),
|
child: Text(item),
|
||||||
))
|
),)
|
||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (String? newValue) {
|
onChanged: (String? newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -718,7 +779,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: AppLocalizations.of(
|
text: AppLocalizations.of(
|
||||||
context)!
|
context,)!
|
||||||
.terms_conditions,
|
.terms_conditions,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
@ -733,14 +794,14 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: AppLocalizations.of(
|
text: AppLocalizations.of(
|
||||||
context)!
|
context,)!
|
||||||
.t_and,
|
.t_and,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black),
|
color: Colors.black,),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: AppLocalizations.of(
|
text: AppLocalizations.of(
|
||||||
context)!
|
context,)!
|
||||||
.privacy_policy,
|
.privacy_policy,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
@ -755,10 +816,10 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: AppLocalizations.of(
|
text: AppLocalizations.of(
|
||||||
context)!
|
context,)!
|
||||||
.conditions,
|
.conditions,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black),
|
color: Colors.black,),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -802,7 +863,7 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
AppLocalizations.of(context)!.change_password,
|
AppLocalizations.of(context)!.change_password,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline),
|
decoration: TextDecoration.underline,),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -841,8 +902,10 @@ class _EditProfileState extends State<EditProfile> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConfirmationDialog extends StatelessWidget {
|
class ConfirmationDialog extends StatelessWidget {
|
||||||
const ConfirmationDialog({Key? key}) : super(key: key);
|
const ConfirmationDialog({super.key});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@ -850,7 +913,7 @@ class ConfirmationDialog extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Are you sure you want to save this page?",
|
'Are you sure you want to save this page?',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Color(0xFF898C81),
|
color: Color(0xFF898C81),
|
||||||
@ -862,7 +925,7 @@ class ConfirmationDialog extends StatelessWidget {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
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(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Color(0xFF898C81),
|
color: Color(0xFF898C81),
|
||||||
@ -871,25 +934,25 @@ class ConfirmationDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: "Country",
|
text: 'Country',
|
||||||
style:
|
style:
|
||||||
TextStyle(fontWeight: FontWeight.w700), // Bold for "name"
|
TextStyle(fontWeight: FontWeight.w700), // Bold for "name"
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: " or ",
|
text: ' or ',
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: "Profile Image",
|
text: 'Profile Image',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w700), // Bold for "date of birth"
|
fontWeight: FontWeight.w700,), // Bold for "date of birth"
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ".",
|
text: '.',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -914,7 +977,7 @@ class ConfirmationDialog extends StatelessWidget {
|
|||||||
), // Set the border color here
|
), // Set the border color here
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Cancel",
|
'Cancel',
|
||||||
style: TextStyle(color: Color(0xFF92722A)),
|
style: TextStyle(color: Color(0xFF92722A)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -939,7 +1002,7 @@ class ConfirmationDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 5,
|
height: 5,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -213,7 +213,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
title: Text(AppLocalizations.of(context)!.feedback_title),
|
title: Text(AppLocalizations.of(context)!.feedback_title),
|
||||||
// appBar: AppBar(
|
// appBar: AppBar(
|
||||||
// backgroundColor: const Color(0xFFf8f9ff),
|
// backgroundColor: const Color(0xFFf8f9ff),
|
||||||
// title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')),
|
// title: Text(AppLocalizations.of(context)!.feedback_title),
|
||||||
// actions: [
|
// actions: [
|
||||||
// Align(
|
// Align(
|
||||||
// alignment: AlignmentDirectional.topEnd,
|
// alignment: AlignmentDirectional.topEnd,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -8,20 +10,15 @@ 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:tutorial_coach_mark/tutorial_coach_mark.dart';
|
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
|
||||||
|
|
||||||
|
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
||||||
|
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
||||||
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
|
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
|
||||||
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
||||||
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
||||||
|
|
||||||
class BaseScaffold extends ConsumerStatefulWidget {
|
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({
|
const BaseScaffold({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.body,
|
required this.body,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.actions,
|
this.actions,
|
||||||
@ -29,7 +26,13 @@ class BaseScaffold extends ConsumerStatefulWidget {
|
|||||||
this.mycenterTitle = false,
|
this.mycenterTitle = false,
|
||||||
this.appbarColor,
|
this.appbarColor,
|
||||||
this.mytitleColor,
|
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
|
@override
|
||||||
_BaseScaffoldState createState() => _BaseScaffoldState();
|
_BaseScaffoldState createState() => _BaseScaffoldState();
|
||||||
@ -56,6 +59,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
final GlobalKey toggleKey = GlobalKey();
|
final GlobalKey toggleKey = GlobalKey();
|
||||||
final GlobalKey drawerKey = GlobalKey();
|
final GlobalKey drawerKey = GlobalKey();
|
||||||
late double toggleHeight=0;
|
late double toggleHeight=0;
|
||||||
|
bool isLoggedOut = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -493,7 +497,6 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to retrieve userId from SharedPreferences
|
|
||||||
Future<String?> getUserId() async {
|
Future<String?> getUserId() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return prefs.getString('userId'); // Retrieve the userId
|
return prefs.getString('userId'); // Retrieve the userId
|
||||||
@ -507,125 +510,39 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
userId = fetchedUserId;
|
userId = fetchedUserId;
|
||||||
});
|
});
|
||||||
//print('NAVUser ID: $userId');
|
//print('NAVUser ID: $userId');
|
||||||
_initializeUserData();
|
_fetchUserData();
|
||||||
} else {
|
} else {
|
||||||
print('No userId found');
|
print('No userId found');
|
||||||
// Handle the case where userId is not available
|
// 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 {
|
Future<void> _fetchUserData() async {
|
||||||
try {
|
try {
|
||||||
// print("Fetching _fetchUserData");
|
|
||||||
final adminAuth = await _pb.admins
|
final adminAuth = await _pb.admins
|
||||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||||
final adminToken = adminAuth.token;
|
final adminToken = adminAuth.token;
|
||||||
|
//print('adminToken- ${adminToken}');
|
||||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||||
userId!,
|
userId!,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer $adminToken',
|
'Authorization': 'Bearer $adminToken',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// print('User details fetched from server: $userDetailsResponse');
|
print('NAVuserDetails: $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));
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
userNames = userData['userNames'];
|
userName = userDetailsResponse.data['uname'] ?? '';
|
||||||
userEmails = userData['userEmails'];
|
userEmail = userDetailsResponse.data['email'] ?? '';
|
||||||
userAvatars = userData['userAvatars'];
|
userAvatar = userDetailsResponse.data['avatar'] ?? '';
|
||||||
roles = userData['roles'];
|
role = userDetailsResponse.data['role'] ?? '';
|
||||||
String recordId = userId;
|
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 =
|
_avatarUrl =
|
||||||
'https://pb.venbait.in/api/files/$collectionIds/$recordId/$userAvatars';
|
'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar';
|
||||||
} else {
|
} else {
|
||||||
_avatarUrl = ''; // Reset to default or empty
|
_avatarUrl = ''; // Reset to default or empty
|
||||||
}
|
}
|
||||||
@ -637,6 +554,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
// setState(() {
|
||||||
|
// isLoggedOut = true;
|
||||||
|
// });
|
||||||
prefs.clear();
|
prefs.clear();
|
||||||
context.go('/');
|
context.go('/');
|
||||||
}
|
}
|
||||||
@ -668,7 +588,8 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
pathColorWhenOff: Colors.grey.shade300,
|
pathColorWhenOff: Colors.grey.shade300,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ref.read(localeProvider.notifier).toggleLocale();
|
ref.read(localeProvider.notifier).toggleLocale();
|
||||||
},),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
leading: Stack(
|
leading: Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -715,7 +636,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: mywidth / 8,
|
width: mywidth / 8,
|
||||||
child: Image(
|
child: Image(
|
||||||
image: AssetImage('assets/logos/fcsc.png'))),
|
image: AssetImage('assets/logos/fcsc.png'),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
@ -736,7 +659,8 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
)
|
)
|
||||||
: Image(
|
: Image(
|
||||||
image: AssetImage(
|
image: AssetImage(
|
||||||
'assets/edit_profile/profile.png'),
|
'assets/edit_profile/profile.png',
|
||||||
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
@ -758,10 +682,10 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -792,7 +716,6 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
key: bottomNavKey,
|
|
||||||
currentIndex: _getSelectedIndex(currentRoute),
|
currentIndex: _getSelectedIndex(currentRoute),
|
||||||
onTap: (index) => _onItemTapped(context, index),
|
onTap: (index) => _onItemTapped(context, index),
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import flutter_secure_storage_macos
|
|||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import sqflite_darwin
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import video_player_avfoundation
|
import video_player_avfoundation
|
||||||
import webview_flutter_wkwebview
|
import webview_flutter_wkwebview
|
||||||
@ -22,6 +23,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||||
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
|
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
|
||||||
|
|||||||
76
pubspec.lock
76
pubspec.lock
@ -134,6 +134,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.3"
|
version: "8.9.3"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.1"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -409,15 +433,23 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: fl_chart
|
name: fl_chart
|
||||||
sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08"
|
sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.69.2"
|
version: "0.70.2"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
flutter_carousel_widget:
|
flutter_carousel_widget:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1252,6 +1284,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
sqflite_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_android
|
||||||
|
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4+6"
|
||||||
|
sqflite_darwin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_darwin
|
||||||
|
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1+1"
|
||||||
|
sqflite_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_platform_interface
|
||||||
|
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -59,6 +59,8 @@ dependencies:
|
|||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
material_charts: ^0.0.23
|
material_charts: ^0.0.23
|
||||||
flutter_staggered_grid_view: ^0.7.0
|
flutter_staggered_grid_view: ^0.7.0
|
||||||
|
flutter_plugin_android_lifecycle: ^2.0.24
|
||||||
|
cached_network_image: ^3.4.1
|
||||||
syncfusion_flutter_charts: ^28.1.39
|
syncfusion_flutter_charts: ^28.1.39
|
||||||
connectivity_plus: ^6.1.2
|
connectivity_plus: ^6.1.2
|
||||||
|
|
||||||
@ -110,7 +112,6 @@ flutter:
|
|||||||
- assets/cached_responses/sharepoint/country_leader_imgs/
|
- assets/cached_responses/sharepoint/country_leader_imgs/
|
||||||
- assets/edit_profile/
|
- assets/edit_profile/
|
||||||
- assets/splash_screen/
|
- assets/splash_screen/
|
||||||
- assets/app_tour/
|
|
||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
- family: Segoe
|
- family: Segoe
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user