develop #1

Merged
akhdanre merged 104 commits from develop into main 2025-07-10 12:38:53 +07:00
6 changed files with 226 additions and 8 deletions
Showing only changes of commit 1c6ce0d023 - Show all commits

View File

@ -22,4 +22,7 @@ class APIEndpoint {
static const String subject = "/subject";
static const String session = "/session";
static const String userData = "/user";
static const String userUpdate = "/user/update";
}

View File

@ -3,23 +3,29 @@ class UserEntity {
final String name;
final String email;
final String? picUrl;
final String? birthDate;
final String? locale;
final String? phone;
UserEntity({
UserEntity({
required this.id,
required this.name,
required this.email,
this.picUrl,
this.birthDate,
this.locale,
this.phone,
});
factory UserEntity.fromJson(Map<String, dynamic> json) {
return UserEntity(
id: json['id'],
name: json['name'],
email: json['email'],
id: json['id'] ?? '',
name: json['name'] ?? '',
email: json['email'] ?? '',
picUrl: json['pic_url'],
birthDate: json['birth_date'],
locale: json['locale'],
phone: json['phone'],
);
}
@ -29,6 +35,7 @@ UserEntity({
'name': name,
'email': email,
'pic_url': picUrl,
'birth_date': birthDate,
'locale': locale,
};
}

View File

@ -0,0 +1,55 @@
class UserFullModel {
final String id;
final String googleId;
final String email;
final String name;
final String birthDate;
final String picUrl;
final String phone;
final String locale;
final String createdAt;
final String updatedAt;
UserFullModel({
required this.id,
required this.googleId,
required this.email,
required this.name,
required this.birthDate,
required this.picUrl,
required this.phone,
required this.locale,
required this.createdAt,
required this.updatedAt,
});
factory UserFullModel.fromJson(Map<String, dynamic> json) {
return UserFullModel(
id: json['id'] ?? '',
googleId: json['google_id'] ?? '',
email: json['email'] ?? '',
name: json['name'] ?? '',
birthDate: json['birth_date'] ?? '',
picUrl: json['pic_url'] ?? '',
phone: json['phone'] ?? '',
locale: json['locale'] ?? '',
createdAt: json['created_at'] ?? '',
updatedAt: json['updated_at'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'google_id': googleId,
'email': email,
'name': name,
'birth_date': birthDate,
'pic_url': picUrl,
'phone': phone,
'locale': locale,
'created_at': createdAt,
'updated_at': updatedAt,
};
}
}

View File

@ -0,0 +1,57 @@
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
import 'package:quiz_app/core/utils/logger.dart';
import 'package:quiz_app/data/models/base/base_model.dart';
import 'package:quiz_app/data/models/user/user_full_model.dart';
import 'package:quiz_app/data/providers/dio_client.dart';
class UserService extends GetxService {
late final Dio _dio;
@override
void onInit() {
_dio = Get.find<ApiClient>().dio;
super.onInit();
}
Future<bool> updateProfileData(String id, String name, {String? birthDate, String? locale, String? phone}) async {
try {
final response = await _dio.post(APIEndpoint.userUpdate, data: {
"id": id,
"name": name,
"birth_date": birthDate,
"locale": locale,
"phone": phone,
});
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} catch (e) {
logC.e("update profile error: $e");
return false;
}
}
Future<BaseResponseModel<UserFullModel>?> getUserData(String id) async {
try {
final response = await _dio.get("${APIEndpoint.userData}/$id");
if (response.statusCode == 200) {
final parsedResponse = BaseResponseModel<UserFullModel>.fromJson(
response.data,
(data) => UserFullModel.fromJson(data),
);
return parsedResponse;
} else {
return null;
}
} catch (e) {
logC.e("get user data error: $e");
return null;
}
}
}

View File

@ -1,9 +1,17 @@
import 'package:get/get.dart';
import 'package:quiz_app/data/controllers/user_controller.dart';
import 'package:quiz_app/data/services/user_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart';
import 'package:quiz_app/feature/profile/controller/update_profile_controller.dart';
class UpdateProfileBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => UpdateProfileController());
Get.lazyPut(() => UserService());
Get.lazyPut(() => UpdateProfileController(
Get.find<UserService>(),
Get.find<UserController>(),
Get.find<UserStorageService>(),
));
}
}

View File

@ -1,20 +1,108 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:quiz_app/core/utils/custom_floating_loading.dart';
import 'package:quiz_app/core/utils/custom_notification.dart';
import 'package:quiz_app/data/controllers/user_controller.dart';
import 'package:quiz_app/data/entity/user/user_entity.dart';
import 'package:quiz_app/data/services/user_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart';
class UpdateProfileController extends GetxController {
final UserController _userController;
final UserStorageService _userStorageService;
final UserService _userService;
UpdateProfileController(
this._userService,
this._userController,
this._userStorageService,
);
final nameController = TextEditingController();
final phoneController = TextEditingController();
final birthDateController = TextEditingController();
var selectedLocale = 'en-US'.obs;
final Map<String, String> localeMap = {
'English': 'en-US',
'Indonesian': 'id-ID',
'French': 'fr-FR',
'Spanish': 'es-ES',
};
void saveProfile() {
Get.snackbar('Success', 'Profile updated successfully');
@override
void onInit() {
final userData = _userController.userData!;
nameController.text = userData.name;
phoneController.text = userData.phone ?? '';
birthDateController.text = userData.birthDate ?? '';
super.onInit();
}
bool _validateInputs() {
final name = nameController.text.trim();
final phone = phoneController.text.trim();
final birthDate = birthDateController.text.trim();
print(birthDate);
if (name.isEmpty || phone.isEmpty || birthDate.isEmpty) {
Get.snackbar('Validation Error', 'All fields must be filled.', snackPosition: SnackPosition.TOP);
return false;
}
if (!_isValidDateFormat(birthDate)) {
Get.snackbar('Validation Error', 'birth date must valid.', snackPosition: SnackPosition.TOP);
return false;
}
return true;
}
Future<void> saveProfile() async {
if (!_validateInputs()) return;
CustomFloatingLoading.showLoadingDialog(Get.context!);
final isSuccessUpdate = await _userService.updateProfileData(
_userController.userData!.id,
nameController.text.trim(),
birthDate: birthDateController.text.trim(),
phone: phoneController.text.trim(),
locale: selectedLocale.value,
);
if (isSuccessUpdate) {
final response = await _userService.getUserData(_userController.userData!.id);
if (response?.data != null) {
final userNew = response!.data!;
final newUser = UserEntity(
id: userNew.id,
email: userNew.email,
name: userNew.name,
birthDate: userNew.birthDate,
locale: userNew.locale,
picUrl: userNew.picUrl,
phone: userNew.phone,
);
_userStorageService.saveUser(newUser);
_userController.userData = newUser;
_userController.email.value = userNew.email;
_userController.userName.value = userNew.name;
_userController.userImage.value = userNew.picUrl;
}
}
Get.back();
CustomNotification.success(title: "Success", message: "Profile updated successfully");
CustomFloatingLoading.hideLoadingDialog(Get.context!);
}
bool _isValidDateFormat(String date) {
final regex = RegExp(r'^([0-2][0-9]|(3)[0-1])\-((0[1-9])|(1[0-2]))\-\d{4}$');
return regex.hasMatch(date);
}
}