connecting API to forgot password, otp screen and reset password, along with making API files into 1 single file
This commit is contained in:
parent
62bc1cb0e2
commit
650e17012f
|
|
@ -0,0 +1,9 @@
|
||||||
|
class ForgotPasswordRequest {
|
||||||
|
final String email;
|
||||||
|
|
||||||
|
const ForgotPasswordRequest({required this.email});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {"email": email};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
class ForgotPasswordResponse {
|
||||||
|
final String status;
|
||||||
|
final String message;
|
||||||
|
final String? token;
|
||||||
|
final int? expired;
|
||||||
|
|
||||||
|
const ForgotPasswordResponse({
|
||||||
|
required this.status,
|
||||||
|
required this.message,
|
||||||
|
this.token,
|
||||||
|
this.expired,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ForgotPasswordResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
final dynamic expiredValue = json["expired"];
|
||||||
|
return ForgotPasswordResponse(
|
||||||
|
status: json["status"]?.toString() ?? "error",
|
||||||
|
message: json["message"]?.toString() ?? "",
|
||||||
|
token: json["token"]?.toString(),
|
||||||
|
expired: expiredValue is num ? expiredValue.toInt() : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isSuccess => status.toLowerCase() == "success";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
class ResetPasswordRequest {
|
||||||
|
final String email;
|
||||||
|
final String token;
|
||||||
|
final String password;
|
||||||
|
|
||||||
|
const ResetPasswordRequest({
|
||||||
|
required this.email,
|
||||||
|
required this.token,
|
||||||
|
required this.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
"email": email,
|
||||||
|
"token": token,
|
||||||
|
"password": password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
class ResetPasswordResponse {
|
||||||
|
final String status;
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const ResetPasswordResponse({
|
||||||
|
required this.status,
|
||||||
|
required this.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ResetPasswordResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ResetPasswordResponse(
|
||||||
|
status: json["status"]?.toString() ?? "error",
|
||||||
|
message: json["message"]?.toString() ?? "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isSuccess => status.toLowerCase() == "success";
|
||||||
|
}
|
||||||
|
|
@ -2,17 +2,20 @@ import 'package:flutter/material.dart';
|
||||||
import '../widgets/custom_textfield.dart';
|
import '../widgets/custom_textfield.dart';
|
||||||
import '../widgets/custom_button.dart';
|
import '../widgets/custom_button.dart';
|
||||||
import '../services/tts_service.dart';
|
import '../services/tts_service.dart';
|
||||||
|
import '../services/forgot_password_api_service.dart';
|
||||||
|
import '../models/forgot_password_request.dart';
|
||||||
import '../utils/colors.dart';
|
import '../utils/colors.dart';
|
||||||
import 'otp_screen.dart';
|
import 'otp_screen.dart';
|
||||||
|
|
||||||
class ForgotPasswordScreen extends StatelessWidget {
|
class ForgotPasswordScreen extends StatelessWidget {
|
||||||
final TextEditingController emailController = TextEditingController();
|
final TextEditingController emailController = TextEditingController();
|
||||||
final TTSService tts = TTSService();
|
final TTSService tts = TTSService();
|
||||||
|
final ForgotPasswordApiService forgotApi = ForgotPasswordApiService();
|
||||||
|
|
||||||
ForgotPasswordScreen({super.key});
|
ForgotPasswordScreen({super.key});
|
||||||
|
|
||||||
// ============ HANDLE RESET PASSWORD ============
|
// ============ HANDLE RESET PASSWORD ============
|
||||||
void handleReset(BuildContext context) {
|
Future<void> handleReset(BuildContext context) async {
|
||||||
String email = emailController.text.trim();
|
String email = emailController.text.trim();
|
||||||
|
|
||||||
// ============ VALIDASI EMAIL KOSONG ============
|
// ============ VALIDASI EMAIL KOSONG ============
|
||||||
|
|
@ -27,11 +30,41 @@ class ForgotPasswordScreen extends StatelessWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tts.speak("OTP telah dikirim ke email Anda");
|
final response = await forgotApi.requestReset(
|
||||||
Navigator.push(
|
ForgotPasswordRequest(email: email),
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (_) => OTPScreen()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isSuccess) {
|
||||||
|
if (response.token == null || response.token!.isEmpty) {
|
||||||
|
tts.speak("Token OTP belum diterima dari server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.expired == null) {
|
||||||
|
tts.speak("Waktu kedaluwarsa OTP belum diterima dari server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "OTP berhasil dikirim");
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => OTPScreen(
|
||||||
|
email: email,
|
||||||
|
serverToken: response.token!,
|
||||||
|
expiredAt: response.expired!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "Email tidak ditemukan");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,40 @@ import 'package:flutter/services.dart';
|
||||||
import '../widgets/custom_textfield.dart';
|
import '../widgets/custom_textfield.dart';
|
||||||
import '../widgets/custom_button.dart';
|
import '../widgets/custom_button.dart';
|
||||||
import '../services/tts_service.dart';
|
import '../services/tts_service.dart';
|
||||||
|
import '../services/forgot_password_api_service.dart';
|
||||||
|
import '../models/forgot_password_request.dart';
|
||||||
import '../utils/colors.dart';
|
import '../utils/colors.dart';
|
||||||
import 'reset_password_screen.dart';
|
import 'reset_password_screen.dart';
|
||||||
|
|
||||||
class OTPScreen extends StatelessWidget {
|
class OTPScreen extends StatefulWidget {
|
||||||
|
final String email;
|
||||||
|
final String serverToken;
|
||||||
|
final int expiredAt;
|
||||||
|
|
||||||
|
const OTPScreen({
|
||||||
|
super.key,
|
||||||
|
required this.email,
|
||||||
|
required this.serverToken,
|
||||||
|
required this.expiredAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OTPScreen> createState() => _OTPScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OTPScreenState extends State<OTPScreen> {
|
||||||
final TextEditingController otpController = TextEditingController();
|
final TextEditingController otpController = TextEditingController();
|
||||||
final TTSService tts = TTSService();
|
final TTSService tts = TTSService();
|
||||||
|
final ForgotPasswordApiService forgotApi = ForgotPasswordApiService();
|
||||||
|
late String currentToken;
|
||||||
|
late int currentExpiredAt;
|
||||||
|
|
||||||
OTPScreen({super.key});
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
currentToken = widget.serverToken;
|
||||||
|
currentExpiredAt = widget.expiredAt;
|
||||||
|
}
|
||||||
|
|
||||||
// ============ VERIFY OTP CODE ============
|
// ============ VERIFY OTP CODE ============
|
||||||
void verifyOTP(BuildContext context) {
|
void verifyOTP(BuildContext context) {
|
||||||
|
|
@ -28,18 +54,73 @@ class OTPScreen extends StatelessWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ LANGSUNG KE RESET PASSWORD (TANPA DATABASE) ============
|
final int nowSeconds = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
// NOTE: Fitur ini bypass validasi karena belum ada database
|
if (nowSeconds > currentExpiredAt) {
|
||||||
|
tts.speak("OTP sudah kedaluwarsa");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otp != currentToken) {
|
||||||
|
tts.speak("OTP tidak sesuai");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tts.speak("Kode OTP valid, lanjut ke reset password");
|
tts.speak("Kode OTP valid, lanjut ke reset password");
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (_) => ResetPasswordScreen()),
|
MaterialPageRoute(
|
||||||
|
builder: (_) => ResetPasswordScreen(
|
||||||
|
email: widget.email,
|
||||||
|
token: currentToken,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> resendOtp() async {
|
||||||
|
final response = await forgotApi.requestReset(
|
||||||
|
ForgotPasswordRequest(email: widget.email),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isSuccess) {
|
||||||
|
if (response.token == null || response.token!.isEmpty) {
|
||||||
|
tts.speak("Token OTP belum diterima dari server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.expired == null) {
|
||||||
|
tts.speak("Waktu kedaluwarsa OTP belum diterima dari server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
currentToken = response.token!;
|
||||||
|
currentExpiredAt = response.expired!;
|
||||||
|
});
|
||||||
|
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "OTP berhasil dikirim ulang");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text("Kode OTP telah dikirim ulang ke email Anda"),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "Gagal mengirim ulang OTP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
tts.speak("Halaman verifikasi kode OTP");
|
tts.speak("Halaman verifikasi kode OTP");
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: AppColors.background,
|
||||||
|
|
@ -134,7 +215,7 @@ class OTPScreen extends StatelessWidget {
|
||||||
|
|
||||||
// 📌 INFO TEXT
|
// 📌 INFO TEXT
|
||||||
Text(
|
Text(
|
||||||
"Kode ini hanya berlaku selama 1 menit setelah dikirim",
|
"Kode ini hanya berlaku selama 2 menit setelah dikirim",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey[500],
|
color: Colors.grey[500],
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -165,14 +246,7 @@ class OTPScreen extends StatelessWidget {
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
tts.speak("Kode OTP telah dikirim ulang");
|
resendOtp();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text("Kode OTP telah dikirim ulang ke email Anda"),
|
|
||||||
backgroundColor: Colors.green,
|
|
||||||
duration: Duration(seconds: 2),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Kirim Ulang Kode",
|
"Kirim Ulang Kode",
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,20 @@ import 'package:flutter/material.dart';
|
||||||
import '../widgets/custom_textfield.dart';
|
import '../widgets/custom_textfield.dart';
|
||||||
import '../widgets/custom_button.dart';
|
import '../widgets/custom_button.dart';
|
||||||
import '../services/tts_service.dart';
|
import '../services/tts_service.dart';
|
||||||
|
import '../services/reset_password_api_service.dart';
|
||||||
|
import '../models/reset_password_request.dart';
|
||||||
import '../utils/colors.dart';
|
import '../utils/colors.dart';
|
||||||
import 'login_screen.dart';
|
import 'login_screen.dart';
|
||||||
|
|
||||||
class ResetPasswordScreen extends StatefulWidget {
|
class ResetPasswordScreen extends StatefulWidget {
|
||||||
const ResetPasswordScreen({super.key});
|
final String email;
|
||||||
|
final String token;
|
||||||
|
|
||||||
|
const ResetPasswordScreen({
|
||||||
|
super.key,
|
||||||
|
required this.email,
|
||||||
|
required this.token,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ResetPasswordScreen> createState() => _ResetPasswordScreenState();
|
State<ResetPasswordScreen> createState() => _ResetPasswordScreenState();
|
||||||
|
|
@ -16,6 +25,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||||
final TextEditingController newPasswordController = TextEditingController();
|
final TextEditingController newPasswordController = TextEditingController();
|
||||||
final TextEditingController confirmPasswordController = TextEditingController();
|
final TextEditingController confirmPasswordController = TextEditingController();
|
||||||
final TTSService tts = TTSService();
|
final TTSService tts = TTSService();
|
||||||
|
final ResetPasswordApiService resetApi = ResetPasswordApiService();
|
||||||
String passwordStrength = ""; // Track password strength
|
String passwordStrength = ""; // Track password strength
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -91,7 +101,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ HANDLE RESET PASSWORD ============
|
// ============ HANDLE RESET PASSWORD ============
|
||||||
void resetPassword(BuildContext context) {
|
Future<void> resetPassword(BuildContext context) async {
|
||||||
String newPass = newPasswordController.text.trim();
|
String newPass = newPasswordController.text.trim();
|
||||||
String confirmPass = confirmPasswordController.text.trim();
|
String confirmPass = confirmPasswordController.text.trim();
|
||||||
|
|
||||||
|
|
@ -113,13 +123,32 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tts.speak("Password berhasil diubah, silakan login kembali");
|
final response = await resetApi.resetPassword(
|
||||||
|
ResetPasswordRequest(
|
||||||
Navigator.pushAndRemoveUntil(
|
email: widget.email,
|
||||||
context,
|
token: widget.token,
|
||||||
MaterialPageRoute(builder: (_) => LoginScreen()),
|
password: newPass,
|
||||||
(route) => false,
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isSuccess) {
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "Password berhasil diubah, silakan login kembali");
|
||||||
|
Navigator.pushAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (_) => LoginScreen()),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tts.speak(response.message.isNotEmpty
|
||||||
|
? response.message
|
||||||
|
: "Token tidak valid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class ApiConfig {
|
||||||
|
// Ubah ke true jika memakai emulator Android (10.0.2.2).
|
||||||
|
static const bool useAndroidEmulator = false;
|
||||||
|
|
||||||
|
// Ganti IP ini dengan IP laptop/PC Anda saat menggunakan HP fisik (ADB).
|
||||||
|
static const String deviceHost = "192.168.0.31:8000";
|
||||||
|
|
||||||
|
static String get baseUrl {
|
||||||
|
if (Platform.isAndroid && useAndroidEmulator) {
|
||||||
|
return "http://10.0.2.2:8000";
|
||||||
|
}
|
||||||
|
return "http://$deviceHost";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import '../models/forgot_password_request.dart';
|
||||||
|
import '../models/forgot_password_response.dart';
|
||||||
|
import 'api_config.dart';
|
||||||
|
|
||||||
|
class ForgotPasswordApiService {
|
||||||
|
Future<ForgotPasswordResponse> requestReset(
|
||||||
|
ForgotPasswordRequest request,
|
||||||
|
) async {
|
||||||
|
final Uri url = Uri.parse("${ApiConfig.baseUrl}/forgot-password");
|
||||||
|
|
||||||
|
try {
|
||||||
|
final http.Response response = await http.post(
|
||||||
|
url,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode(request.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, dynamic> jsonBody =
|
||||||
|
jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
return ForgotPasswordResponse.fromJson(jsonBody);
|
||||||
|
} catch (error) {
|
||||||
|
return const ForgotPasswordResponse(
|
||||||
|
status: "error",
|
||||||
|
message: "Gagal terhubung ke server",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,27 +1,15 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../models/register_request.dart';
|
import '../models/register_request.dart';
|
||||||
import '../models/register_response.dart';
|
import '../models/register_response.dart';
|
||||||
|
import 'api_config.dart';
|
||||||
|
|
||||||
class RegisterApiService {
|
class RegisterApiService {
|
||||||
// Ubah nilai ini jika ingin memakai emulator Android (10.0.2.2).
|
|
||||||
static const bool _useAndroidEmulator = false;
|
|
||||||
|
|
||||||
// Ganti IP ini dengan IP laptop/PC Anda saat menggunakan HP fisik (ADB).
|
|
||||||
static const String _deviceHost = "192.168.18.14:8000";
|
|
||||||
|
|
||||||
static String get _baseUrl {
|
|
||||||
if (Platform.isAndroid && _useAndroidEmulator) {
|
|
||||||
return "http://10.0.2.2:8000";
|
|
||||||
}
|
|
||||||
return "http://$_deviceHost";
|
|
||||||
}
|
|
||||||
static const String _tokenKey = "auth_token";
|
static const String _tokenKey = "auth_token";
|
||||||
|
|
||||||
Future<RegisterResponse> register(RegisterRequest request) async {
|
Future<RegisterResponse> register(RegisterRequest request) async {
|
||||||
final Uri url = Uri.parse("$_baseUrl/register");
|
final Uri url = Uri.parse("${ApiConfig.baseUrl}/register");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final http.Response response = await http.post(
|
final http.Response response = await http.post(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import '../models/reset_password_request.dart';
|
||||||
|
import '../models/reset_password_response.dart';
|
||||||
|
import 'api_config.dart';
|
||||||
|
|
||||||
|
class ResetPasswordApiService {
|
||||||
|
Future<ResetPasswordResponse> resetPassword(
|
||||||
|
ResetPasswordRequest request,
|
||||||
|
) async {
|
||||||
|
final Uri url = Uri.parse("${ApiConfig.baseUrl}/reset-password");
|
||||||
|
|
||||||
|
try {
|
||||||
|
final http.Response response = await http.post(
|
||||||
|
url,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode(request.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.body.isEmpty) {
|
||||||
|
return ResetPasswordResponse(
|
||||||
|
status: "error",
|
||||||
|
message:
|
||||||
|
"Respons server kosong (status ${response.statusCode})",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> jsonBody;
|
||||||
|
try {
|
||||||
|
jsonBody = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
} catch (_) {
|
||||||
|
return ResetPasswordResponse(
|
||||||
|
status: "error",
|
||||||
|
message:
|
||||||
|
"Respons server tidak valid (status ${response.statusCode})",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResetPasswordResponse parsed =
|
||||||
|
ResetPasswordResponse.fromJson(jsonBody);
|
||||||
|
|
||||||
|
if (response.statusCode >= 400 && parsed.message.isEmpty) {
|
||||||
|
return ResetPasswordResponse(
|
||||||
|
status: "error",
|
||||||
|
message:
|
||||||
|
"Gagal reset password (status ${response.statusCode})",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
} catch (error) {
|
||||||
|
return const ResetPasswordResponse(
|
||||||
|
status: "error",
|
||||||
|
message: "Gagal terhubung ke server",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue