diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a0293d1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,307 @@
+
+# Binar Final Project - Skyfly Android
+
+
+
+
+
+
+
+### Team C-1 Skyfly!
+
+Skyfly API allows you to get the needed resources to make Skyfly application run seamlessly. Some of this service is using authentication to access each service. You need to login first to access the service.
+
+### Overview
+
+SkyFly is an online flight ticket booking application designed to provide a seamless and user-friendly experience for booking flights. This Android application supports various functionalities essential for an efficient flight booking system.
+
+# Data Team C1
+
+| | | **LinkedIn** | **Github** |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------- |
+| **FSW** | _Reinanda Faris_ | [LinkedIn](https://www.linkedin.com/in/reinanda-faris/) | [Github](https://github.com/Reinandafaris) |
+| | _Viery Nugroho_ | [LinkedIn](https://www.linkedin.com/in/viery-nugroho) | [Github](https://github.com/vierynugroho) |
+| | _Andhika Rizky Aulia_ | [LinkedIn](https://www.linkedin.com/in/andhika-rizky/) | [Github](https://github.com/ndikrp) |
+| | _Ananda Ias Falah_ | [LinkedIn](https://www.linkedin.com/in/falahsuryagemilang/) | [Github](https://github.com/falahsg) |
+| | _Naufal Ady Saputro_ | [LinkedIn](https://www.linkedin.com/in/naufal-ady-saputro-71050b24b/) | [Github](https://github.com/naufaladysaputro) |
+| | _Rizki Mauludin Yoga P._ | [LinkedIn](https://www.linkedin.com/in/riski-mauludin-yoga-8718602b0/) | [Github](https://github.com/RMYP) |
+| | _Rafi Husein Bagaskara_ | [LinkedIn](https://www.linkedin.com/in/rafi-husein-257a76291) | [Github](https://github.com/raisenbk) |
+| | _Lowis Armando Hutabarat_ | [LinkedIn](www.linkedin.com/in/lowis-armando-hutabarat-80b7612b3) | [Github](https://github.com/LowisHutabarat) |
+| | |
+| **AND** | _Komang Yuda Saputra_ | [LinkedIn](https://www.linkedin.com/in/komang-yuda-saputra-abb21b291/) | [Github](https://github.com/YudaSaputraa) |
+| | _Ihsan Widagdo_ | [LinkedIn](https://www.linkedin.com/in/ihsan-widagdo/) | [Github](https://github.com/dagdo03) |
+| | _Bella Febriany Nawangsari_ | [LinkedIn](https://www.linkedin.com/in/bella-febriany-nawangsari-4642a3291/) | [Github](https://github.com/bellafebrianyn) |
+| | _Mochammad Yusuf Pratama_ | | |
+| | |
+| **Project Title** | _SKY-FLY_ |
+| | |
+| **Note** | _Binar KM6_ |
+| | [Trello Team C1 Binar KM6](https://trello.com/c/2XzOhXim/60-c1-binar-km6-fsw-x-and) |
+| | [Daily Stand-Up Team C1 Binar KM6](https://docs.google.com/spreadsheets/d/1aCpje7mQnG5uhmBOh9sEThQKYgatLdNpPoSoQK6VUvk/edit#gid=1785037003) |
+| | [Deployed API](https://backend-skyfly-c1.vercel.app/api-docs/) |
+
+---
+
+
+
+
+## Tech Stack
+
+- MVVM (Model View ViewModel)
+- Repository Pattern
+- Coroutine Flow
+- Koin Dependency Injection
+- Coil Image Loader
+- Retrofit
+- OkHttp
+- Groupie
+- Room
+- Chucker
+- JUnit 4
+- Firebase
+- Postman
+
+## How to Use the App
+
+### Initial Setup
+- **Onboarding Screen**: The first time you install the app, an onboarding screen will be displayed.
+- **Home Tab**: After completing the onboarding, you'll be navigated to the Home tab.
+
+### Home Tab
+- **Selecting Flight Details**:
+ - Choose the origin and destination.
+ - Select the departure date.
+ - Specify the number of passengers and seat class.
+- **Viewing Available Flights**:
+ - Once you fill out the form, you'll be directed to the available flights page.
+ - Select a flight to view its details.
+- **Flight Detail**:
+ - After selecting a flight, you will be taken to the flight detail screen.
+- **Authentication**:
+ - If you're not logged in, you'll be prompted to log in or register.
+ - You can create a new account or log in if you already have one.
+- **Ticket and Passenger Form**:
+ - Fill out the ticket booking form and the passenger details form.
+- **Seat Selection**:
+ - Navigate to the seat view to choose your desired seat.
+- **Flight Detail Review**:
+ - Review the flight details you've selected.
+- **Payment**:
+ - Proceed to the payment menu, where various payment methods are available, such as transfer and QRIS.
+- **Ticket Confirmation**:
+ - After completing the payment, you'll see the detailed ticket information.
+
+### History Tab
+- **Viewing Ticket History**:
+ - All your ticket booking history will be displayed here.
+ - Click on any booking to view its details.
+- **Transaction Status**:
+ - The tab shows the status of your transactions: "PAID", "UNPAID", or "CANCELED".
+ - If you have unpaid bookings and want to cancel them, click the "cancel transaction" button below the details.
+
+### Notification Tab
+- **Viewing Notifications**:
+ - View all notifications related to your account.
+ - Click on a notification to see more details.
+
+### Account Tab
+- **Viewing and Editing Profile**:
+ - View your personal information and edit details such as your name and phone number.
+- **Changing Password**:
+ - Click the button to change your password.
+- **Logging Out**:
+ - Use the logout button if you wish to log out of your account.
+
+
+
+## SkyFly Api used
+
+#### Login
+
+```http
+POST api/v1/auth/login
+```
+
+| Query | Type | Description |
+| :-------- | :------- | :------------------------- |
+| `loginRequest` | `object` | **Required**. Login request payload (email, password) |
+
+
+
+#### Register
+
+```http
+POST api/v1/auth/register
+
+```
+| Query | Type | Description |
+| :---------------- | :------- | :------------------------------- |
+| `registerRequest` | `object` | **Required**. Register request payload (FullName, Email, PhoneNumber, password) |
+
+
+
+#### Verify Account
+
+```http
+PUT api/v1/auth/verified
+```
+
+| Query | Type | Description |
+| :---------------------- | :------- |:---------------------------------------------------|
+| `token` | `string` | **Required**. Verification token |
+| `verifyAccountRequest` | `object` | **Required**. Verify account request payload (otp) |
+
+
+
+#### Forget Password
+
+```http
+POST api/v1/auth/forgetPassword
+```
+
+| Parameter | Type | Description |
+| :----------------------- | :------- |:------------------------------------------------------|
+| `forgetPasswordRequest` | `object` | **Required**. Forget password request payload (email) |
+
+
+#### Get All Flight with Sort or Filter
+
+```http
+GET api/v1/flights/
+```
+
+| Query | Type | Description |
+| :----------------- | :------- | :----------------------------- |
+| `search` | `string` | Search query |
+| `page` | `int` | Page number for pagination |
+| `limit` | `int` | Number of results per page |
+| `departureAirport` | `string` | Departure airport code |
+| `arrivalAirport` | `string` | Arrival airport code |
+| `departureDate` | `string` | Departure date (YYYY-MM-DD) |
+| `returnDate` | `string` | Return date (YYYY-MM-DD) |
+| `arrivalDate` | `string` | Arrival date (YYYY-MM-DD) |
+| `seatClass` | `string` | Seat class |
+| `adult` | `int` | Number of adult passengers |
+| `children` | `int` | Number of child passengers |
+| `baby` | `int` | Number of baby passengers |
+| `sort` | `string` | Sorting order |
+
+
+#### Get Flight by Id
+
+```http
+GET api/v1/flights/{id}
+
+```
+
+| Parameter | Type | Description |
+| :---------- | :------- | :----------------------------- |
+| `id` | `string` | **Required**. Flight ID |
+| `seatClass` | `string` | Seat class |
+
+
+#### Get All Airtport
+
+```http
+GET api/v1/airports/
+```
+
+| Query | Type | Description |
+| :------ | :-------- | :------------------------------------ |
+| `city` | `string` | City name |
+| `showAll` | `boolean` | Show all airports (default: true) |
+
+
+### Get All Flight Seat by Flight id
+
+```http
+GET api/v1/flightSeats/flight/{id}
+```
+
+| Parameter | Type | Description |
+| :-------- | :------- | :----------------------------- |
+| `id` | `string` | **Required**. Flight ID |
+| `limit` | `int` | Number of results per page |
+
+
+
+
+
+### Get User Profile
+
+```http
+GET api/v1/auth/me
+```
+
+| Query | Type | Description |
+| :-------- | :------- | :----------------------------- |
+| `token` | `string` | **Required**. Verification token |
+
+
+### Update User Profile
+
+```http
+PATCH api/v1/auth/me
+```
+
+| Query | Type | Description |
+| :-------- | :------- |:-------------------------------------------------------------------|
+| `token` | `string` | **Required**. Verification token |
+| `updateProfileRequest` | `object` | **Required**. Update profile request payload (FullName/phoneNumber |
+
+
+### Create Transaction
+
+```http
+ POST api/v1/transactions/payment
+```
+
+| Query | Type | Description |
+| :----------------- | :------- | :----------------------------- |
+| `token` | `string` | **Required**. Verification token |
+| `flightId` | `string` | **Required**. Flight ID |
+| `adult` | `int` | **Required**. Number of adults |
+| `child` | `int` | **Required**. Number of children |
+| `baby` | `int` | **Required**. Number of babies |
+| `transactionRequest` | `object` | **Required**. Transaction request payload |
+
+### Get All Notification
+```http
+GET api/v1/notifications
+```
+| Query | Type | Description |
+| :----- | :------- | :----------------------------- |
+| `token` | `string` | **Required**. Verification token |
+| `limit` | `int` | Number of results per page |
+
+
+### Get Transaction by Id
+```http
+ GET api/v1/transactions/{id}
+```
+| Parameter | Type | Description |
+| :-------- | :------- | :----------------------------- |
+| `token` | `string` | **Required**. Verification token |
+| `id` | `string` | **Required**. Transaction ID |
+
+
+### Get All Transaction History, filter and search
+
+```http
+ GET api/v1/transactions
+```
+| Query | Type | Description |
+| :------------ | :------- | :----------------------------- |
+| `token` | `string` | **Required**. Verification token |
+| `limit` | `int` | Number of results per page |
+| `startDate` | `string` | Start date for filtering (YYYY-MM-DD) |
+| `endDate` | `string` | End date for filtering (YYYY-MM-DD) |
+| `flightCode` | `string` | Flight code for filtering |
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 0e1e27a..80b65f7 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -115,6 +115,7 @@ dependencies {
implementation(libs.firebase.crashlytics)
implementation(libs.firebase.perf)
testImplementation(libs.junit)
+ testImplementation(libs.junit.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -131,6 +132,8 @@ dependencies {
// Local storage
implementation(libs.room.ktx)
+ androidTestImplementation(project(":app"))
+ androidTestImplementation(project(":app"))
ksp(libs.room.compiler)
// Coroutine
diff --git a/app/src/main/java/com/kom/skyfly/core/BaseActivity.kt b/app/src/main/java/com/kom/skyfly/core/BaseActivity.kt
index 98c5df3..e506129 100644
--- a/app/src/main/java/com/kom/skyfly/core/BaseActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/core/BaseActivity.kt
@@ -1,8 +1,14 @@
package com.kom.skyfly.core
import android.content.Intent
+import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
+import com.kom.skyfly.R
+import com.kom.skyfly.presentation.bottomsheetsdialog.NotLoginBottomSheets
import com.kom.skyfly.presentation.login.LoginActivity
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
+import es.dmoral.toasty.Toasty
import org.koin.androidx.viewmodel.ext.android.viewModel
/**
@@ -12,7 +18,20 @@ Github : https://github.com/YudaSaputraa
open class BaseActivity : AppCompatActivity() {
private val baseViewModel: BaseViewModel by viewModel()
- fun handleUnAuthorize() {
+ fun errorHandler(e: Exception) {
+ if (e is UnAuthorizeException) {
+ baseViewModel.clearSession()
+ openNotLoggedInModal()
+ } else if (e is ServerErrorException) {
+ Toasty.error(
+ this,
+ getString(R.string.text_server_error_please_try_again_later),
+ Toast.LENGTH_SHORT,
+ ).show()
+ }
+ }
+
+ fun doLogoutHandler() {
baseViewModel.clearSession()
navigateToLogin()
}
@@ -22,4 +41,11 @@ open class BaseActivity : AppCompatActivity() {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
+
+ private fun openNotLoggedInModal() {
+ if (!supportFragmentManager.isStateSaved) {
+ val bottomSheetFragment = NotLoginBottomSheets()
+ bottomSheetFragment.show(supportFragmentManager, bottomSheetFragment.tag)
+ }
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/auth/AuthDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/auth/AuthDataSource.kt
index 6b5c1fc..45acfa8 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/auth/AuthDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/auth/AuthDataSource.kt
@@ -7,6 +7,7 @@ import com.kom.skyfly.data.source.network.model.login.LoginResponse
import com.kom.skyfly.data.source.network.model.register.RegisterRequest
import com.kom.skyfly.data.source.network.model.register.RegisterResponse
import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpResponse
+import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpSmsRequest
import com.kom.skyfly.data.source.network.model.userprofile.UserProfileResponse
import com.kom.skyfly.data.source.network.model.verifyaccount.VerifyAccountRequest
import com.kom.skyfly.data.source.network.model.verifyaccount.VerifyAccountResponse
@@ -44,6 +45,12 @@ interface AuthDataSource {
@Throws(exceptionClasses = [Exception::class])
suspend fun resendOtpRequest(token: String): ResendOtpResponse
+ @Throws(exceptionClasses = [Exception::class])
+ suspend fun resendOtpSmsRequest(
+ token: String,
+ phoneNumber: String,
+ ): ResendOtpResponse
+
suspend fun isUserLoggedIn(): UserProfileResponse
}
@@ -90,6 +97,18 @@ class AuthDataSourceImpl(private val service: SkyFlyApiService) : AuthDataSource
return service.resendOtp(token)
}
+ override suspend fun resendOtpSmsRequest(
+ token: String,
+ phoneNumber: String,
+ ): ResendOtpResponse {
+ return try {
+ val resendOtpSmsRequest = ResendOtpSmsRequest(phoneNumber)
+ service.resendOtpSms(token, resendOtpSmsRequest)
+ } catch (e: Exception) {
+ throw e
+ }
+ }
+
override suspend fun isUserLoggedIn(): UserProfileResponse {
return service.getUserProfile()
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/destinationfavorite/DestinationFavoriteDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/destinationfavorite/DestinationFavoriteDataSource.kt
deleted file mode 100644
index dab6f48..0000000
--- a/app/src/main/java/com/kom/skyfly/data/datasource/destinationfavorite/DestinationFavoriteDataSource.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.kom.skyfly.data.datasource.destinationfavorite
-
-import com.kom.skyfly.data.model.destinationfavorite.DestinationFavorite
-
-/**
-Written by Komang Yuda Saputra
-Github : https://github.com/YudaSaputraa
- **/
-interface DestinationFavoriteDataSource {
- fun getAllDestinationFavorite(): List
-}
-
-class DestinationFavoriteDataSourceImpl() : DestinationFavoriteDataSource {
- override fun getAllDestinationFavorite(): List {
- return mutableListOf(
- DestinationFavorite(
- origin = "Jakarta",
- destination = "Bangkok",
- airline = "AirAsia",
- dateRange = "20 - 30 Maret 2023",
- price = "IDR 950.000",
- isLimited = false,
- discount = null,
- imageUrl = "https://images.unsplash.com/photo-1508009603885-50cf7c579365?q=80&w=2850&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "Sydney",
- airline = "AirAsia",
- dateRange = "5 - 25 Maret 2023",
- price = "IDR 3.650.000",
- isLimited = false,
- discount = "50% OFF",
- imageUrl = "https://images.unsplash.com/photo-1506973035872-a4ec16b8e8d9?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "Tokyo",
- airline = "Garuda Indonesia",
- dateRange = "1 - 10 April 2023",
- price = "IDR 4.500.000",
- isLimited = true,
- discount = null,
- imageUrl = "https://images.unsplash.com/photo-1513407030348-c983a97b98d8?q=80&w=2944&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "Seoul",
- airline = "Korean Air",
- dateRange = "15 - 25 April 2023",
- price = "IDR 3.800.000",
- isLimited = false,
- discount = "30% OFF",
- imageUrl = "https://images.unsplash.com/photo-1506816561089-5cc37b3aa9b0?q=80&w=3075&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "Paris",
- airline = "Air France",
- dateRange = "5 - 15 Mei 2023",
- price = "IDR 8.000.000",
- isLimited = true,
- discount = null,
- imageUrl = "https://images.unsplash.com/photo-1502602898657-3e91760cbb34?q=80&w=2946&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "New York",
- airline = "American Airlines",
- dateRange = "10 - 20 Juni 2023",
- price = "IDR 10.000.000",
- isLimited = false,
- discount = "40% OFF",
- imageUrl = "https://images.unsplash.com/photo-1602828889956-45ec6cee6758?q=80&w=2832&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- DestinationFavorite(
- origin = "Jakarta",
- destination = "London",
- airline = "British Airways",
- dateRange = "20 - 30 Juni 2023",
- price = "IDR 9.000.000",
- isLimited = true,
- discount = null,
- imageUrl = "https://images.unsplash.com/photo-1513635269975-59663e0ac1ad?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- ),
- )
- }
-}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/history/FlighCodeListener.kt b/app/src/main/java/com/kom/skyfly/data/datasource/history/FlighCodeListener.kt
new file mode 100644
index 0000000..a5b5731
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/history/FlighCodeListener.kt
@@ -0,0 +1,5 @@
+package com.kom.skyfly.data.datasource.history
+
+interface FlightCodeListener {
+ fun onFlightCodeEntered(flightCode: String)
+}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/history/HistoryDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/history/HistoryDataSource.kt
index d72cba6..041fe0b 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/history/HistoryDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/history/HistoryDataSource.kt
@@ -1,280 +1,26 @@
package com.kom.skyfly.data.datasource.history
-import com.kom.skyfly.data.model.history.Data
-import com.kom.skyfly.data.model.history.SectionedDate
+import com.kom.skyfly.data.source.network.model.history.HistoryResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
interface HistoryDataSource {
- fun getHistoryData(): List
-
-// fun getHistoryDetailById(id: String): HistoryDetail?
+ suspend fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ): HistoryResponse
}
-class HistoryDataSourceImpl() : HistoryDataSource {
- override fun getHistoryData(): List {
- return mutableListOf(
- SectionedDate(
- date = "Maret 2020",
- data =
- listOf(
- Data(
- status = "Unpaid",
- departureLocation = "Yogyakarta",
- departureDate = "2020-03-10",
- departureTime = "10:00",
- flightDuration = "1h 30m",
- destination = "Denpasar",
- arrivalDate = "2020-03-10",
- arrivalTime = "11:30",
- bookingCode = "GH789",
- flightClass = "Business",
- total = "IDR 3.000.000",
- departureStatus = "On Time",
- departureAirport = "Adisucipto International Airport",
- departureTerminal = "Terminal 2",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF123",
- passengerName = "John Doe",
- passengerId = "ABC123",
- destinationAirport = "Ngurah Rai International Airport",
- arrivalStatus = "On Time",
- totalPassenger = "2",
- price = 2500000,
- tax = 50000,
- ),
- Data(
- status = "Paid",
- departureLocation = "Bandung",
- departureDate = "2020-03-20",
- departureTime = "13:00",
- flightDuration = "1h 10m",
- destination = "Surabaya",
- arrivalDate = "2020-03-20",
- arrivalTime = "14:10",
- bookingCode = "IJ012",
- flightClass = "Economy",
- total = "IDR 1.200.000",
- departureStatus = "Delayed",
- departureAirport = "Husein Sastranegara International Airport",
- departureTerminal = "Terminal 1",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF456",
- passengerName = "Jane Doe",
- passengerId = "DEF456",
- destinationAirport = "Juanda International Airport",
- arrivalStatus = "Delayed",
- totalPassenger = "1",
- price = 1000000,
- tax = 20000,
- ),
- Data(
- status = "Cancelled",
- departureLocation = "Jakarta",
- departureDate = "2020-03-25",
- departureTime = "09:30",
- flightDuration = "2h 20m",
- destination = "Surabaya",
- arrivalDate = "2020-03-25",
- arrivalTime = "11:50",
- bookingCode = "MN345",
- flightClass = "First",
- total = "IDR 4.500.000",
- departureStatus = "Cancelled",
- departureAirport = "Soekarno-Hatta International Airport",
- departureTerminal = "Terminal 3",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF789",
- passengerName = "Alice Smith",
- passengerId = "GHI789",
- destinationAirport = "Juanda International Airport",
- arrivalStatus = "Cancelled",
- totalPassenger = "3",
- price = 4000000,
- tax = 50000,
- ),
- ),
- ),
- SectionedDate(
- date = "April 2020",
- data =
- listOf(
- Data(
- status = "Unpaid",
- departureLocation = "Bandung",
- departureDate = "2020-04-05",
- departureTime = "08:00",
- flightDuration = "1h 45m",
- destination = "Medan",
- arrivalDate = "2020-04-05",
- arrivalTime = "09:45",
- bookingCode = "OP678",
- flightClass = "Business",
- total = "IDR 3.500.000",
- departureStatus = "On Time",
- departureAirport = "Husein Sastranegara International Airport",
- departureTerminal = "Terminal 1",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF890",
- passengerName = "Michael Johnson",
- passengerId = "JKL012",
- destinationAirport = "Kualanamu International Airport",
- arrivalStatus = "On Time",
- totalPassenger = "2",
- price = 3000000,
- tax = 50000,
- ),
- Data(
- status = "Paid",
- departureLocation = "Surabaya",
- departureDate = "2020-04-10",
- departureTime = "10:30",
- flightDuration = "1h 20m",
- destination = "Bandung",
- arrivalDate = "2020-04-10",
- arrivalTime = "11:50",
- bookingCode = "QR901",
- flightClass = "Economy",
- total = "IDR 1.000.000",
- departureStatus = "On Time",
- departureAirport = "Juanda International Airport",
- departureTerminal = "Terminal 2",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF345",
- passengerName = "Emily Brown",
- passengerId = "MNO345",
- destinationAirport = "Husein Sastranegara International Airport",
- arrivalStatus = "On Time",
- totalPassenger = "1",
- price = 800000,
- tax = 20000,
- ),
- Data(
- status = "Cancelled",
- departureLocation = "Yogyakarta",
- departureDate = "2020-04-15",
- departureTime = "11:00",
- flightDuration = "2h",
- destination = "Denpasar",
- arrivalDate = "2020-04-15",
- arrivalTime = "13:00",
- bookingCode = "ST456",
- flightClass = "First",
- total = "IDR 5.000.000",
- departureStatus = "Cancelled",
- departureAirport = "Adisucipto International Airport",
- departureTerminal = "Terminal 2",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF678",
- passengerName = "David Wilson",
- passengerId = "PQR678",
- destinationAirport = "Ngurah Rai International Airport",
- arrivalStatus = "Cancelled",
- totalPassenger = "3",
- price = 4500000,
- tax = 50000,
- ),
- ),
- ),
- SectionedDate(
- date = "Mei 2020",
- data =
- listOf(
- Data(
- status = "Paid",
- departureLocation = "Medan",
- departureDate = "2020-05-05",
- departureTime = "12:00",
- flightDuration = "2h 30m",
- destination = "Surabaya",
- arrivalDate = "2020-05-05",
- arrivalTime = "14:30",
- bookingCode = "UV123",
- flightClass = "Business",
- total = "IDR 4.000.000",
- departureStatus = "On Time",
- departureAirport = "Kualanamu International Airport",
- departureTerminal = "Terminal 1",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF901",
- passengerName = " Lisa Johnson",
- passengerId = "STU901",
- destinationAirport = "Juanda International Airport",
- arrivalStatus = "On Time",
- totalPassenger = "2",
- price = 3500000,
- tax = 50000,
- ),
- Data(
- status = "Unpaid",
- departureLocation = "Denpasar",
- departureDate = "2020-05-10",
- departureTime = "08:30",
- flightDuration = "1h 15m",
- destination = "Yogyakarta",
- arrivalDate = "2020-05-10",
- arrivalTime = "09:45",
- bookingCode = "WX234",
- flightClass = "Economy",
- total = "IDR 1.800.000",
- departureStatus = "On Time",
- departureAirport = "Ngurah Rai International Airport",
- departureTerminal = "Terminal 2",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF012",
- passengerName = "Chris Martin",
- passengerId = "VWX012",
- destinationAirport = "Adisucipto International Airport",
- arrivalStatus = "On Time",
- totalPassenger = "1",
- price = 1500000,
- tax = 30000,
- ),
- Data(
- status = "Cancelled",
- departureLocation = "Surabaya",
- departureDate = "2020-05-15",
- departureTime = "11:30",
- flightDuration = "2h",
- destination = "Bandung",
- arrivalDate = "2020-05-15",
- arrivalTime = "13:30",
- bookingCode = "YZ345",
- flightClass = "First",
- total = "IDR 6.500.000",
- departureStatus = "Cancelled",
- departureAirport = "Juanda International Airport",
- departureTerminal = "Terminal 2",
- airlineName = "SkyFly Airlines",
- flightNumber = "SF234",
- passengerName = "Emma Watson",
- passengerId = "YZA345",
- destinationAirport = "Husein Sastranegara International Airport",
- arrivalStatus = "Cancelled",
- totalPassenger = "3",
- price = 6000000,
- tax = 50000,
- ),
- ),
- ),
- )
+class HistoryDataSourceImpl(
+ private val service: SkyFlyApiService,
+) : HistoryDataSource {
+ override suspend fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ): HistoryResponse {
+ return service.getAllTransactionHistory(limit, startDate, endDate, flightCode)
}
-
-// override fun getHistoryDetailById(id: String): HistoryDetail? {
-// val allHistoryData = getHistoryData().flatMap { it.data }
-// return allHistoryData.find { it.id == id }?.let { data ->
-// HistoryDetail(
-// id = data.id, // Gunakan id dari data yang sesuai
-// paymentStatus = data.status,
-// bookingCode = data.bookingCode,
-// departureTime = data.departureTime,
-// departureDate = data.departureDate,
-// flightClass = data.flightClass,
-// arrivalTime = data.arrivalTime,
-// arrivalDate = data.arrivalDate,
-// total = data.total.toInt(), // Kami asumsikan total dalam format String dan perlu dikonversi ke Int
-// departureLocation = data.departureLocation,
-// destination = data.destination,
-// flightDuration = data.flightDuration,
-// )
-// }
-// }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSource.kt
index bffb893..0e8c4a1 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSource.kt
@@ -1,11 +1,13 @@
package com.kom.skyfly.data.datasource.home
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
import com.kom.skyfly.data.source.network.model.home.airport.AirportResponse
+import com.kom.skyfly.data.source.network.model.home.favourite_destination.FavouriteDestinationResponse
import com.kom.skyfly.data.source.network.model.home.flight.FlightResponse
import com.kom.skyfly.data.source.network.model.home.flight_detail.FlightDetailResponse
interface HomeDataSource {
- suspend fun getAllAirports(): AirportResponse
+ suspend fun getAllAirports(city: String? = null): AirportResponse
suspend fun getAllFlight(
search: String? = null,
@@ -13,11 +15,21 @@ interface HomeDataSource {
departureAirport: String,
arrivalAirport: String,
departureDate: String,
- seatClass: String,
+ seatClass: String?,
+ limit: Int? = 20,
+ returnDate: String?,
+ arrivalDate: String? = null,
+ adult: Int? = 1,
+ children: Int? = 0,
+ baby: Int? = 0,
): FlightResponse
suspend fun getDetailFlight(
id: String,
- seatClass: String,
+ seatClass: String?,
): FlightDetailResponse
+
+ fun getSeatClassData(): List
+
+ suspend fun getDestinationFavourites(): FavouriteDestinationResponse
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSourceImpl.kt b/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSourceImpl.kt
index ad503f7..5cad879 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSourceImpl.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/home/HomeDataSourceImpl.kt
@@ -1,6 +1,8 @@
package com.kom.skyfly.data.datasource.home
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
import com.kom.skyfly.data.source.network.model.home.airport.AirportResponse
+import com.kom.skyfly.data.source.network.model.home.favourite_destination.FavouriteDestinationResponse
import com.kom.skyfly.data.source.network.model.home.flight.FlightResponse
import com.kom.skyfly.data.source.network.model.home.flight_detail.FlightDetailResponse
import com.kom.skyfly.data.source.network.services.SkyFlyApiService
@@ -8,8 +10,8 @@ import com.kom.skyfly.data.source.network.services.SkyFlyApiService
class HomeDataSourceImpl(
private val service: SkyFlyApiService,
) : HomeDataSource {
- override suspend fun getAllAirports(): AirportResponse {
- return service.getAllAirports()
+ override suspend fun getAllAirports(city: String?): AirportResponse {
+ return service.getAllAirports(city = city)
}
override suspend fun getAllFlight(
@@ -18,22 +20,50 @@ class HomeDataSourceImpl(
departureAirport: String,
arrivalAirport: String,
departureDate: String,
- seatClass: String,
+ seatClass: String?,
+ limit: Int?,
+ returnDate: String?,
+ arrivalDate: String?,
+ adult: Int?,
+ children: Int?,
+ baby: Int?,
): FlightResponse {
return service.getAllFlights(
search = search,
page = page,
departureAirport = departureAirport,
arrivalAirport = arrivalAirport,
+ returnDate = returnDate,
departureDate = departureDate,
seatClass = seatClass,
+ adult = adult,
+ children = children,
+ baby = baby,
)
}
override suspend fun getDetailFlight(
id: String,
- seatClass: String,
+ seatClass: String?,
): FlightDetailResponse {
return service.getDetailFlightById(id = id, seatClass = seatClass)
}
+
+ override fun getSeatClassData(): List {
+ return mutableListOf(
+ SeatClassHome(
+ seatClassName = "ECONOMY",
+ ),
+ SeatClassHome(
+ seatClassName = "BUSINESS",
+ ),
+ SeatClassHome(
+ seatClassName = "FIRST",
+ ),
+ )
+ }
+
+ override suspend fun getDestinationFavourites(): FavouriteDestinationResponse {
+ return service.getDestinationFavourite()
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/profiles/ProfileDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/profiles/ProfileDataSource.kt
index 2084993..450a301 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/profiles/ProfileDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/profiles/ProfileDataSource.kt
@@ -15,6 +15,7 @@ interface ProfileDataSource {
suspend fun updateUserProfile(
name: String?,
phoneNumber: String?,
+ familyName: String?,
password: String?,
confirmPassword: String?,
): UpdateProfileResponse
@@ -28,10 +29,11 @@ class ProfileDataSourceImpl(private val service: SkyFlyApiService) : ProfileData
override suspend fun updateUserProfile(
name: String?,
phoneNumber: String?,
+ familyName: String?,
password: String?,
confirmPassword: String?,
): UpdateProfileResponse {
- val updateProfileRequest = UpdateProfileRequest(name, phoneNumber, password, confirmPassword)
+ val updateProfileRequest = UpdateProfileRequest(name, phoneNumber, familyName, password, confirmPassword)
return service.updateUserProfile(updateProfileRequest)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryDataSource.kt
index a557f56..aa465b4 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryDataSource.kt
@@ -1,6 +1,7 @@
package com.kom.skyfly.data.datasource.searchhistory
import com.kom.skyfly.data.source.local.database.dao.SearchHistoryDao
+import com.kom.skyfly.data.source.local.database.entity.SearchDestinationHistoryEntity
import com.kom.skyfly.data.source.local.database.entity.SearchHistoryEntity
import kotlinx.coroutines.flow.Flow
@@ -11,23 +12,52 @@ Github : https://github.com/YudaSaputraa
interface SearchHistoryDataSource {
fun getAllSearchHistory(): Flow>
+ fun getAllSearchDestinationHistory(): Flow>
+
suspend fun insertSearchHistory(searchHistory: SearchHistoryEntity): Long
+ suspend fun insertSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Long
+
suspend fun updateSearchHistory(searchHistory: SearchHistoryEntity): Int
+ suspend fun updateSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int
+
suspend fun deleteSearchHistory(searchHistory: SearchHistoryEntity): Int
+ suspend fun deleteSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int
+
suspend fun deleteAllSearchHistory()
+
+ suspend fun deleteAllSearchDestinationHistory()
}
class SearchHistoryDataSourceImpl(private val dao: SearchHistoryDao) : SearchHistoryDataSource {
override fun getAllSearchHistory(): Flow> = dao.getAllSearchHistory()
+ override fun getAllSearchDestinationHistory(): Flow> = dao.getAllSearchDestinationHistory()
+
override suspend fun insertSearchHistory(searchHistory: SearchHistoryEntity): Long = dao.insertSearchHistory(searchHistory)
+ override suspend fun insertSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Long =
+ dao.insertSearchDestinationHistory(
+ searchDestinationHistory = searchDestinationHistory,
+ )
+
override suspend fun updateSearchHistory(searchHistory: SearchHistoryEntity): Int = dao.updateSearchHistory(searchHistory)
+ override suspend fun updateSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int =
+ dao.updateSearchDestinationHistory(
+ searchDestinationHistory = searchDestinationHistory,
+ )
+
override suspend fun deleteSearchHistory(searchHistory: SearchHistoryEntity): Int = dao.deleteSearchHistory(searchHistory)
+ override suspend fun deleteSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int =
+ dao.deleteSearchDestinationHistory(
+ searchDestinationHistory = searchDestinationHistory,
+ )
+
override suspend fun deleteAllSearchHistory() = dao.deleteAll()
+
+ override suspend fun deleteAllSearchDestinationHistory() = dao.deleteAllRecentSearchDestination()
}
diff --git a/app/src/main/java/com/kom/skyfly/data/datasource/transaction/TransactionDataSource.kt b/app/src/main/java/com/kom/skyfly/data/datasource/transaction/TransactionDataSource.kt
index 78bc8f8..8932853 100644
--- a/app/src/main/java/com/kom/skyfly/data/datasource/transaction/TransactionDataSource.kt
+++ b/app/src/main/java/com/kom/skyfly/data/datasource/transaction/TransactionDataSource.kt
@@ -1,5 +1,7 @@
package com.kom.skyfly.data.datasource.transaction
+import com.kom.skyfly.data.source.network.model.paymentstatus.PaymentStatusResponse
+import com.kom.skyfly.data.source.network.model.transaction.cancel.CancelTransactionResponse
import com.kom.skyfly.data.source.network.model.transaction.detail.TransactionDetailResponse
import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
import com.kom.skyfly.data.source.network.model.transaction.response.TransactionResponse
@@ -19,6 +21,10 @@ interface TransactionDataSource {
): TransactionResponse
suspend fun getTransactionById(id: String): TransactionDetailResponse
+
+ suspend fun getPaymentStatus(id: String): PaymentStatusResponse
+
+ suspend fun cancelTransaction(id: String): CancelTransactionResponse
}
class TransactionDataSourceImpl(private val service: SkyFlyApiService) : TransactionDataSource {
@@ -35,4 +41,12 @@ class TransactionDataSourceImpl(private val service: SkyFlyApiService) : Transac
override suspend fun getTransactionById(id: String): TransactionDetailResponse {
return service.getTransactionById(id)
}
+
+ override suspend fun getPaymentStatus(id: String): PaymentStatusResponse {
+ return service.getPaymentStatus(id)
+ }
+
+ override suspend fun cancelTransaction(id: String): CancelTransactionResponse {
+ return service.cancelTransaction(id)
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/mapper/CancelTransactionMapper.kt b/app/src/main/java/com/kom/skyfly/data/mapper/CancelTransactionMapper.kt
new file mode 100644
index 0000000..4332233
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/mapper/CancelTransactionMapper.kt
@@ -0,0 +1,21 @@
+package com.kom.skyfly.data.mapper
+
+import com.kom.skyfly.data.model.transaction.cancel.ItemsCancelTransactionModel
+import com.kom.skyfly.data.source.network.model.transaction.cancel.ItemsCancelTransactionResponse
+
+/**
+Written by Komang Yuda Saputra
+Github : https://github.com/YudaSaputraa
+ **/
+fun ItemsCancelTransactionResponse?.toCancelTransaction() =
+ ItemsCancelTransactionModel(
+ currency = this?.currency.orEmpty(),
+ grossAmount = this?.grossAmount.orEmpty(),
+ merchantId = this?.merchantId.orEmpty(),
+ orderId = this?.orderId.orEmpty(),
+ paymentStatus = this?.paymentStatus.orEmpty(),
+ paymentType = this?.paymentType.orEmpty(),
+ transactionId = this?.transactionId.orEmpty(),
+ transactionStatus = this?.transactionStatus.orEmpty(),
+ transactionTime = this?.transactionTime.orEmpty(),
+ )
diff --git a/app/src/main/java/com/kom/skyfly/data/mapper/HistoryTransactionMapper.kt b/app/src/main/java/com/kom/skyfly/data/mapper/HistoryTransactionMapper.kt
new file mode 100644
index 0000000..07ba087
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/mapper/HistoryTransactionMapper.kt
@@ -0,0 +1,170 @@
+package com.kom.skyfly.data.mapper
+
+import com.kom.skyfly.data.model.history.new.AirlineDomain
+import com.kom.skyfly.data.model.history.new.ArrivalDomain
+import com.kom.skyfly.data.model.history.new.BookingDomain
+import com.kom.skyfly.data.model.history.new.DepartureAirportDomain
+import com.kom.skyfly.data.model.history.new.DepartureDomain
+import com.kom.skyfly.data.model.history.new.DestinationAirportDomain
+import com.kom.skyfly.data.model.history.new.FlightDomain
+import com.kom.skyfly.data.model.history.new.HistoryDomain
+import com.kom.skyfly.data.model.history.new.ItemsHistoryDomain
+import com.kom.skyfly.data.model.history.new.SeatDomain
+import com.kom.skyfly.data.model.history.new.TransactionDetailDomain
+import com.kom.skyfly.data.model.history.new.TransactionDomain
+import com.kom.skyfly.data.source.network.model.history.Airline
+import com.kom.skyfly.data.source.network.model.history.Arrival
+import com.kom.skyfly.data.source.network.model.history.Booking
+import com.kom.skyfly.data.source.network.model.history.Departure
+import com.kom.skyfly.data.source.network.model.history.DepartureAirport
+import com.kom.skyfly.data.source.network.model.history.DestinationAirport
+import com.kom.skyfly.data.source.network.model.history.Flight
+import com.kom.skyfly.data.source.network.model.history.HistoryResponse
+import com.kom.skyfly.data.source.network.model.history.ItemsHistoryResponse
+import com.kom.skyfly.data.source.network.model.history.Seat
+import com.kom.skyfly.data.source.network.model.history.Transaction
+import com.kom.skyfly.data.source.network.model.history.TransactionDetail
+
+fun HistoryResponse?.toHistoryDomain(): HistoryDomain =
+ this?.let {
+ HistoryDomain(
+ data = it.data.toItemsHistoryDomainList(),
+ message = it.message.orEmpty(),
+ status = it.status ?: false,
+ totalItems = it.totalItems ?: 0,
+ )
+ } ?: HistoryDomain(emptyList(), "", false, 0)
+
+fun List?.toItemsHistoryDomainList(): List =
+ this?.mapNotNull { it.toItemsHistoryDomain() } ?: emptyList()
+
+fun ItemsHistoryResponse?.toItemsHistoryDomain(): ItemsHistoryDomain? =
+ this?.let {
+ ItemsHistoryDomain(
+ date = it.date.orEmpty(),
+ transactions = it.transactions.toTransactionDomainList(),
+ )
+ }
+
+fun List?.toTransactionDomainList(): List = this?.mapNotNull { it.toTransactionDomain() } ?: emptyList()
+
+fun Transaction?.toTransactionDomain(): TransactionDomain? =
+ this?.let {
+ TransactionDomain(
+ booking = it.booking.toBookingDomain(),
+ id = it.id.orEmpty(),
+ orderId = it.orderId.orEmpty(),
+ status = it.status.orEmpty(),
+ tax = it.tax ?: 0,
+ totalPrice = it.totalPrice ?: 0,
+ transactionDetail = it.transactionDetail.toTransactionDetailDomainList(),
+ userId = it.userId.orEmpty(),
+ )
+ }
+
+fun List?.toTransactionDetailDomainList(): List =
+ this?.mapNotNull { it.toTransactionDetailDomain() } ?: emptyList()
+
+fun TransactionDetail?.toTransactionDetailDomain(): TransactionDetailDomain? =
+ this?.let {
+ it.flight.toFlightDomain()?.let { flight ->
+ TransactionDetailDomain(
+ citizenship = it.citizenship.orEmpty(),
+ dob = it.dob.orEmpty(),
+ familyName = it.familyName.orEmpty(),
+ flight = flight,
+ id = it.id.orEmpty(),
+ issuingCountry = it.issuingCountry.orEmpty(),
+ name = it.name.orEmpty(),
+ passengerCategory = it.passengerCategory.orEmpty(),
+ passport = it.passport.orEmpty(),
+ seat = it.seat.toSeatDomain(),
+ totalPrice = it.totalPrice ?: 0,
+ transactionId = it.transactionId.orEmpty(),
+ validityPeriod = it.validityPeriod.orEmpty(),
+ )
+ }
+ }
+
+fun Airline?.toAirlineDomain(): AirlineDomain =
+ AirlineDomain(
+ code = this?.code.orEmpty(),
+ id = this?.id.orEmpty(),
+ image = this?.image.orEmpty(),
+ name = this?.name.orEmpty(),
+ terminal = this?.terminal.orEmpty(),
+ )
+
+fun Arrival?.toArrivalDomain(): ArrivalDomain =
+ ArrivalDomain(
+ date = this?.date.orEmpty(),
+ time = this?.time.orEmpty(),
+ )
+
+fun Booking?.toBookingDomain(): BookingDomain =
+ BookingDomain(
+ code = this?.code.orEmpty(),
+ date = this?.date.orEmpty(),
+ time = this?.time.orEmpty(),
+ )
+
+fun Departure?.toDepartureDomain(): DepartureDomain =
+ DepartureDomain(
+ date = this?.date.orEmpty(),
+ time = this?.time.orEmpty(),
+ )
+
+fun DepartureAirport?.toDepartureAirportDomain(): DepartureAirportDomain =
+ DepartureAirportDomain(
+ city = this?.city.orEmpty(),
+ code = this?.code.orEmpty(),
+ continent = this?.continent.orEmpty(),
+ country = this?.country.orEmpty(),
+ id = this?.id.orEmpty(),
+ image = this?.image.orEmpty(),
+ name = this?.name.orEmpty(),
+ )
+
+fun DestinationAirport?.toDestinationAirportDomain(): DestinationAirportDomain =
+ DestinationAirportDomain(
+ city = this?.city.orEmpty(),
+ code = this?.code.orEmpty(),
+ continent = this?.continent.orEmpty(),
+ country = this?.country.orEmpty(),
+ id = this?.id.orEmpty(),
+ image = this?.image.orEmpty(),
+ name = this?.name.orEmpty(),
+ )
+
+fun Flight?.toFlightDomain(): FlightDomain? =
+ this?.airline?.toAirlineDomain()?.let {
+ this.arrival?.toArrivalDomain()?.let { arrival ->
+ this.departure?.toDepartureDomain()?.let { departure ->
+ this.departureAirport?.toDepartureAirportDomain()?.let { departureAirport ->
+ this.destinationAirport?.toDestinationAirportDomain()?.let { destinationAirport ->
+ FlightDomain(
+ airline = it,
+ arrival = arrival,
+ code = this.code.orEmpty(),
+ departure = departure,
+ departureAirport = departureAirport,
+ destinationAirport = destinationAirport,
+ flightDuration = this.flightDuration.orEmpty(),
+ flightPrice = this.flightPrice ?: 0,
+ id = this.id.orEmpty(),
+ )
+ }
+ }
+ }
+ }
+ }
+
+fun Seat?.toSeatDomain(): SeatDomain =
+ SeatDomain(
+ flightId = this?.flightId.orEmpty(),
+ id = this?.id.orEmpty(),
+ seatNumber = this?.seatNumber.orEmpty(),
+ seatPrice = this?.seatPrice ?: 0,
+ status = this?.status.orEmpty(),
+ type = this?.type.orEmpty(),
+ )
diff --git a/app/src/main/java/com/kom/skyfly/data/mapper/PaymentStatusMapper.kt b/app/src/main/java/com/kom/skyfly/data/mapper/PaymentStatusMapper.kt
new file mode 100644
index 0000000..688a44e
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/mapper/PaymentStatusMapper.kt
@@ -0,0 +1,49 @@
+package com.kom.skyfly.data.mapper
+
+import com.kom.skyfly.data.model.transaction.paymentstatus.ItemsPaymentStatus
+import com.kom.skyfly.data.model.transaction.paymentstatus.PaymentStatusModel
+import com.kom.skyfly.data.model.transaction.paymentstatus.VaNumberModel
+import com.kom.skyfly.data.source.network.model.paymentstatus.ItemsPaymentStatusResponse
+import com.kom.skyfly.data.source.network.model.paymentstatus.PaymentStatusResponse
+import com.kom.skyfly.data.source.network.model.paymentstatus.VaNumber
+
+/**
+Written by Komang Yuda Saputra
+Github : https://github.com/YudaSaputraa
+ **/
+fun PaymentStatusResponse?.toPaymentStatus(): PaymentStatusModel =
+ this?.let {
+ PaymentStatusModel(
+ data = it.data.toItemsPaymentStatusDomain(),
+ message = it.message.orEmpty(),
+ status = it.status ?: false,
+ )
+ } ?: PaymentStatusModel(null, "", false)
+
+fun ItemsPaymentStatusResponse?.toItemsPaymentStatusDomain(): ItemsPaymentStatus? =
+ this?.let {
+ ItemsPaymentStatus(
+ currency = it.currency.orEmpty(),
+ expiryTime = it.expiryTime.orEmpty(),
+ grossAmount = it.grossAmount.orEmpty(),
+ merchantId = it.merchantId.orEmpty(),
+ orderId = it.orderId.orEmpty(),
+ paymentStatus = it.paymentStatus.orEmpty(),
+ paymentType = it.paymentType.orEmpty(),
+ signatureKey = it.signatureKey.orEmpty(),
+ transactionId = it.transactionId.orEmpty(),
+ transactionStatus = it.transactionStatus.orEmpty(),
+ transactionTime = it.transactionTime.orEmpty(),
+ vaNumbers = it.vaNumbers.toVaNumberDomainList(),
+ )
+ }
+
+fun List?.toVaNumberDomainList(): List = this?.mapNotNull { it.toVaNumberDomain() } ?: emptyList()
+
+fun VaNumber?.toVaNumberDomain(): VaNumberModel? =
+ this?.let {
+ VaNumberModel(
+ bank = it.bank.orEmpty(),
+ vaNumber = it.vaNumber.orEmpty(),
+ )
+ }
diff --git a/app/src/main/java/com/kom/skyfly/data/mapper/SearchHistoryMapper.kt b/app/src/main/java/com/kom/skyfly/data/mapper/SearchHistoryMapper.kt
index 03bd354..444f1f5 100644
--- a/app/src/main/java/com/kom/skyfly/data/mapper/SearchHistoryMapper.kt
+++ b/app/src/main/java/com/kom/skyfly/data/mapper/SearchHistoryMapper.kt
@@ -1,6 +1,8 @@
package com.kom.skyfly.data.mapper
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
import com.kom.skyfly.data.model.searchhistory.SearchHistory
+import com.kom.skyfly.data.source.local.database.entity.SearchDestinationHistoryEntity
import com.kom.skyfly.data.source.local.database.entity.SearchHistoryEntity
/**
@@ -21,4 +23,18 @@ fun SearchHistoryEntity?.toSearchHistory() =
searchHistory = this?.searchHistory.orEmpty(),
)
+fun SearchDestinationHistory?.toSearchDestinationHistoryEntity() =
+ SearchDestinationHistoryEntity(
+ id = this?.id ?: 0,
+ searchDestinationHistory = this?.searchDestinationHistory.orEmpty(),
+ )
+
+fun SearchDestinationHistoryEntity?.toSearchDestinationHistory() =
+ SearchDestinationHistory(
+ id = this?.id ?: 0,
+ searchDestinationHistory = this?.searchDestinationHistory.orEmpty(),
+ )
+
fun List?.toSearchHistoryList() = this?.map { it.toSearchHistory() }
+
+fun List?.toSearchDestinationHistoryList() = this?.map { it.toSearchDestinationHistory() }
diff --git a/app/src/main/java/com/kom/skyfly/data/mapper/home/FlightTicketMapper.kt b/app/src/main/java/com/kom/skyfly/data/mapper/home/FlightTicketMapper.kt
index a3f3cc2..1133cf6 100644
--- a/app/src/main/java/com/kom/skyfly/data/mapper/home/FlightTicketMapper.kt
+++ b/app/src/main/java/com/kom/skyfly/data/mapper/home/FlightTicketMapper.kt
@@ -1,8 +1,10 @@
package com.kom.skyfly.data.mapper.home
+import com.kom.skyfly.data.model.home.destination_favourite.DestinationFavourite
import com.kom.skyfly.data.model.home.flight.FlightTicket
import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
import com.kom.skyfly.data.source.network.model.common.FlightData
+import com.kom.skyfly.data.source.network.model.home.favourite_destination.FlightDetailResponse
import com.kom.skyfly.data.source.network.model.home.flight_detail.FlightDetailData
fun FlightData?.toFlightTicket() =
@@ -24,6 +26,9 @@ fun FlightData?.toFlightTicket() =
airplaneImg = this?.plane?.image.orEmpty(),
facilities = this?.facilities.orEmpty(),
departureTerminal = this?.plane?.terminal.orEmpty(),
+ code = this?.plane?.code.orEmpty(),
+ departureDate = this?.departureDate.orEmpty(),
+ arrivalDate = this?.arrivalDate.orEmpty(),
)
fun FlightDetailData?.toFlightDetailTicket() =
@@ -39,8 +44,8 @@ fun FlightDetailData?.toFlightDetailTicket() =
arrivalTime = this?.arrivalTime.orEmpty(),
transitNotes = this?.transit?.status ?: false,
duration = this?.duration.orEmpty(),
- price = this?.price ?: 0,
- seatClass = "ECONOMY",
+ price = this?.seatClass?.first()?.seatPrice ?: 0,
+ seatClass = this?.seatClass?.first()?.seatClassName.orEmpty(),
airplaneName = this?.plane?.name.orEmpty(),
airplaneImg = this?.plane?.image.orEmpty(),
facilities =
@@ -54,6 +59,20 @@ fun FlightDetailData?.toFlightDetailTicket() =
code = this?.code.orEmpty(),
)
+fun FlightDetailResponse?.toDestinationFavourite() =
+ DestinationFavourite(
+ id = this?.flightDetails?.flightId.orEmpty(),
+ departureDate = this?.flightDetails?.sourceDestination?.departureDate.orEmpty(),
+ departureCity = this?.flightDetails?.sourceDestination?.departureCity.orEmpty(),
+ arrivalDate = this?.flightDetails?.arrivalDestination?.arrivalDate.orEmpty(),
+ arrivalCity = this?.flightDetails?.arrivalDestination?.arrivalCity.orEmpty(),
+ price = this?.flightDetails?.plane?.price ?: 0,
+ img = this?.flightDetails?.arrivalDestination?.image.orEmpty(),
+ airline = this?.flightDetails?.plane?.airline.orEmpty(),
+ )
+
fun Collection?.toFlightTickets() = this?.map { it.toFlightTicket() } ?: listOf()
fun Collection?.toFlightDetailTickets() = this?.map { it.toFlightDetailTicket() } ?: listOf()
+
+fun Collection?.toDestinationFavourites() = this?.map { it.toDestinationFavourite() } ?: listOf()
diff --git a/app/src/main/java/com/kom/skyfly/data/model/destinationfavorite/DestinationFavorite.kt b/app/src/main/java/com/kom/skyfly/data/model/destinationfavorite/DestinationFavorite.kt
deleted file mode 100644
index 9a089ec..0000000
--- a/app/src/main/java/com/kom/skyfly/data/model/destinationfavorite/DestinationFavorite.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.kom.skyfly.data.model.destinationfavorite
-
-import java.util.UUID
-
-/**
-Written by Komang Yuda Saputra
-Github : https://github.com/YudaSaputraa
- **/
-data class DestinationFavorite(
- val id: String? = UUID.randomUUID().toString(),
- val origin: String?,
- val destination: String?,
- val airline: String?,
- val dateRange: String?,
- val price: String?,
- val isLimited: Boolean?,
- val discount: String?,
- val imageUrl: String?,
-)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/history/Data.kt b/app/src/main/java/com/kom/skyfly/data/model/history/Data.kt
deleted file mode 100644
index 08945f9..0000000
--- a/app/src/main/java/com/kom/skyfly/data/model/history/Data.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.kom.skyfly.data.model.history
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-import java.util.UUID
-
-@Parcelize
-data class Data(
- val id: String = UUID.randomUUID().toString(),
- val status: String,
- val departureLocation: String,
- val departureDate: String,
- val departureTime: String,
- val flightDuration: String,
- val destination: String,
- val arrivalDate: String,
- val arrivalTime: String,
- val bookingCode: String,
- val flightClass: String,
- val total: String,
- val departureStatus: String,
- val departureAirport: String,
- val departureTerminal: String,
- val airlineName: String,
- val flightNumber: String,
- val passengerName: String,
- val passengerId: String,
- val destinationAirport: String,
- val arrivalStatus: String,
- val totalPassenger: String,
- val price: Int,
- val tax: Int,
-) : Parcelable
diff --git a/app/src/main/java/com/kom/skyfly/data/model/history/SectionedDate.kt b/app/src/main/java/com/kom/skyfly/data/model/history/SectionedDate.kt
deleted file mode 100644
index 5321d43..0000000
--- a/app/src/main/java/com/kom/skyfly/data/model/history/SectionedDate.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.kom.skyfly.data.model.history
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class SectionedDate(
- val date: String,
- val data: List,
-) : Parcelable
diff --git a/app/src/main/java/com/kom/skyfly/data/model/history/new/new.kt b/app/src/main/java/com/kom/skyfly/data/model/history/new/new.kt
new file mode 100644
index 0000000..4245245
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/history/new/new.kt
@@ -0,0 +1,92 @@
+package com.kom.skyfly.data.model.history.new
+
+data class AirlineDomain(
+ val code: String,
+ val id: String,
+ val image: String,
+ val name: String,
+ val terminal: String,
+)
+
+data class ArrivalDomain(val date: String, val time: String)
+
+data class BookingDomain(val code: String, val date: String, val time: String)
+
+data class DepartureDomain(val date: String, val time: String)
+
+data class DepartureAirportDomain(
+ val city: String,
+ val code: String,
+ val continent: String,
+ val country: String,
+ val id: String,
+ val image: String,
+ val name: String,
+)
+
+data class DestinationAirportDomain(
+ val city: String,
+ val code: String,
+ val continent: String,
+ val country: String,
+ val id: String,
+ val image: String,
+ val name: String,
+)
+
+data class FlightDomain(
+ val airline: AirlineDomain,
+ val arrival: ArrivalDomain,
+ val code: String,
+ val departure: DepartureDomain,
+ val departureAirport: DepartureAirportDomain,
+ val destinationAirport: DestinationAirportDomain,
+ val flightDuration: String,
+ val flightPrice: Int,
+ val id: String,
+)
+
+data class ItemsHistoryDomain(val date: String, val transactions: List)
+
+data class SeatDomain(
+ val flightId: String,
+ val id: String,
+ val seatNumber: String,
+ val seatPrice: Int,
+ val status: String,
+ val type: String,
+)
+
+data class TransactionDomain(
+ val booking: BookingDomain,
+ val id: String,
+ val orderId: String,
+ val status: String,
+ val tax: Int,
+ val totalPrice: Int,
+ val transactionDetail: List,
+ val userId: String,
+)
+
+data class TransactionDetailDomain(
+ val citizenship: String,
+ val dob: String,
+ val familyName: String,
+ val flight: FlightDomain,
+ val id: String,
+ val issuingCountry: String,
+ val name: String,
+ val passengerCategory: String,
+ val passport: String,
+ val seat: SeatDomain,
+ val totalPrice: Int,
+ val transactionId: String,
+ val validityPeriod: String,
+)
+
+data class HistoryDomain(
+ val data: List,
+ val message: String,
+ val status: Boolean,
+ val totalItems: Int,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/destination_favourite/DestinationFavourite.kt b/app/src/main/java/com/kom/skyfly/data/model/home/destination_favourite/DestinationFavourite.kt
new file mode 100644
index 0000000..4e2cbfe
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/destination_favourite/DestinationFavourite.kt
@@ -0,0 +1,21 @@
+package com.kom.skyfly.data.model.home.destination_favourite
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+/**
+Written by Komang Yuda Saputra
+Github : https://github.com/YudaSaputraa
+ **/
+
+@Parcelize
+data class DestinationFavourite(
+ val id: String?,
+ val departureDate: String?,
+ val departureCity: String?,
+ val arrivalDate: String?,
+ val arrivalCity: String?,
+ val airline: String?,
+ val price: Int?,
+ val img: String?,
+) : Parcelable
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/flight/FlightTicket.kt b/app/src/main/java/com/kom/skyfly/data/model/home/flight/FlightTicket.kt
index f6511fe..349d1c1 100644
--- a/app/src/main/java/com/kom/skyfly/data/model/home/flight/FlightTicket.kt
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/flight/FlightTicket.kt
@@ -7,6 +7,8 @@ import kotlinx.parcelize.Parcelize
data class FlightTicket(
val id: String,
val departureCity: String,
+ val departureDate: String,
+ val arrivalDate: String,
val arrivalCity: String,
val departureAirport: String,
val arrivalAirport: String,
@@ -17,6 +19,7 @@ data class FlightTicket(
val directNotes: Boolean,
val duration: String,
val price: Int,
+ val code: String,
val seatClass: String,
val airplaneName: String,
val airplaneImg: String,
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/flight_detail/FlightDetailTicket.kt b/app/src/main/java/com/kom/skyfly/data/model/home/flight_detail/FlightDetailTicket.kt
index e556a5e..78b9984 100644
--- a/app/src/main/java/com/kom/skyfly/data/model/home/flight_detail/FlightDetailTicket.kt
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/flight_detail/FlightDetailTicket.kt
@@ -1,5 +1,9 @@
package com.kom.skyfly.data.model.home.flight_detail
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
data class FlightDetailTicket(
val id: String,
val departureCity: String,
@@ -21,4 +25,4 @@ data class FlightDetailTicket(
val code: String,
val facilities: String,
val departureTerminal: String,
-)
+) : Parcelable
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/intent/SearchResultIntent.kt b/app/src/main/java/com/kom/skyfly/data/model/home/intent/SearchResultIntent.kt
new file mode 100644
index 0000000..28f4e5f
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/intent/SearchResultIntent.kt
@@ -0,0 +1,15 @@
+package com.kom.skyfly.data.model.home.intent
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class SearchResultIntent(
+ val returnId: String?,
+ val departureId: String?,
+ val seatClass: String?,
+ val adultCount: Int?,
+ val childCount: Int?,
+ val babyCount: Int?,
+ val roundTrip: Boolean?,
+) : Parcelable
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/search_history/SearchDestinationHistory.kt b/app/src/main/java/com/kom/skyfly/data/model/home/search_history/SearchDestinationHistory.kt
new file mode 100644
index 0000000..f0e60e6
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/search_history/SearchDestinationHistory.kt
@@ -0,0 +1,6 @@
+package com.kom.skyfly.data.model.home.search_history
+
+data class SearchDestinationHistory(
+ var id: Int? = null,
+ var searchDestinationHistory: String? = null,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/home/seat_class/SeatClassHome.kt b/app/src/main/java/com/kom/skyfly/data/model/home/seat_class/SeatClassHome.kt
new file mode 100644
index 0000000..8aac0c4
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/home/seat_class/SeatClassHome.kt
@@ -0,0 +1,8 @@
+package com.kom.skyfly.data.model.home.seat_class
+
+import java.util.UUID
+
+data class SeatClassHome(
+ val id: String = UUID.randomUUID().toString(),
+ val seatClassName: String,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/CancelTransactionModel.kt b/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/CancelTransactionModel.kt
new file mode 100644
index 0000000..2019a1b
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/CancelTransactionModel.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.model.transaction.cancel
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class CancelTransactionModel(
+ @SerializedName("data")
+ val data: ItemsCancelTransactionModel?,
+ @SerializedName("message")
+ val message: String?,
+ @SerializedName("status")
+ val status: Boolean?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/ItemsCancelTransactionModel.kt b/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/ItemsCancelTransactionModel.kt
new file mode 100644
index 0000000..8afdd17
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/transaction/cancel/ItemsCancelTransactionModel.kt
@@ -0,0 +1,26 @@
+package com.kom.skyfly.data.model.transaction.cancel
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class ItemsCancelTransactionModel(
+ @SerializedName("currency")
+ val currency: String?,
+ @SerializedName("gross_amount")
+ val grossAmount: String?,
+ @SerializedName("merchant_id")
+ val merchantId: String?,
+ @SerializedName("order_id")
+ val orderId: String?,
+ @SerializedName("payment_status")
+ val paymentStatus: String?,
+ @SerializedName("payment_type")
+ val paymentType: String?,
+ @SerializedName("transaction_id")
+ val transactionId: String?,
+ @SerializedName("transaction_status")
+ val transactionStatus: String?,
+ @SerializedName("transaction_time")
+ val transactionTime: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/ItemsPaymentStatus.kt b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/ItemsPaymentStatus.kt
new file mode 100644
index 0000000..981113b
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/ItemsPaymentStatus.kt
@@ -0,0 +1,19 @@
+package com.kom.skyfly.data.model.transaction.paymentstatus
+
+import androidx.annotation.Keep
+
+@Keep
+data class ItemsPaymentStatus(
+ val currency: String?,
+ val expiryTime: String?,
+ val grossAmount: String?,
+ val merchantId: String?,
+ val orderId: String?,
+ val paymentStatus: String?,
+ val paymentType: String?,
+ val signatureKey: String?,
+ val transactionId: String?,
+ val transactionStatus: String?,
+ val transactionTime: String?,
+ val vaNumbers: List?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/PaymentStatusModel.kt b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/PaymentStatusModel.kt
new file mode 100644
index 0000000..5539841
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/PaymentStatusModel.kt
@@ -0,0 +1,10 @@
+package com.kom.skyfly.data.model.transaction.paymentstatus
+
+import androidx.annotation.Keep
+
+@Keep
+data class PaymentStatusModel(
+ val data: ItemsPaymentStatus?,
+ val message: String?,
+ val status: Boolean?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/VaNumberModel.kt b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/VaNumberModel.kt
new file mode 100644
index 0000000..a6b16d0
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/model/transaction/paymentstatus/VaNumberModel.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.model.transaction.paymentstatus
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class VaNumberModel(
+ @SerializedName("bank")
+ val bank: String?,
+ @SerializedName("va_number")
+ val vaNumber: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/auth/AuthRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/auth/AuthRepository.kt
index 63aed99..e94bfc6 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/auth/AuthRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/auth/AuthRepository.kt
@@ -44,6 +44,12 @@ interface AuthRepository {
@Throws(exceptionClasses = [Exception::class])
fun resendOtpRequest(token: String): Flow>
+ @Throws(exceptionClasses = [Exception::class])
+ fun resendOtpSmsRequest(
+ token: String,
+ phoneNumber: String,
+ ): Flow>
+
fun isUserLoggedIn(): Flow>
}
@@ -81,6 +87,13 @@ class AuthRepositoryImpl(private val dataSource: AuthDataSource) : AuthRepositor
return proceedFlow { dataSource.resendOtpRequest(token) }
}
+ override fun resendOtpSmsRequest(
+ token: String,
+ phoneNumber: String,
+ ): Flow> {
+ return proceedFlow { dataSource.resendOtpSmsRequest(token, phoneNumber) }
+ }
+
override fun isUserLoggedIn(): Flow> {
return proceedFlow { dataSource.isUserLoggedIn().toUserIsLoggedIn() }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/destinationfavorite/DestinationFavoriteRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/destinationfavorite/DestinationFavoriteRepository.kt
deleted file mode 100644
index cf6dd94..0000000
--- a/app/src/main/java/com/kom/skyfly/data/repository/destinationfavorite/DestinationFavoriteRepository.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.kom.skyfly.data.repository.destinationfavorite
-
-import com.kom.skyfly.data.datasource.destinationfavorite.DestinationFavoriteDataSource
-import com.kom.skyfly.data.model.destinationfavorite.DestinationFavorite
-import com.kom.skyfly.utils.ResultWrapper
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
-
-/**
-Written by Komang Yuda Saputra
-Github : https://github.com/YudaSaputraa
- **/
-interface DestinationFavoriteRepository {
- fun getAllDestinationFavorite(): Flow>>
-}
-
-class DestinationFavoriteRepositoryImpl(private val destinationFavoriteDataSource: DestinationFavoriteDataSource) :
- DestinationFavoriteRepository {
- override fun getAllDestinationFavorite(): Flow>> {
- return flow {
- emit(ResultWrapper.Loading())
- delay(1000)
- val result = destinationFavoriteDataSource.getAllDestinationFavorite()
- emit(ResultWrapper.Success(result))
- }
- }
-}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/history/HistoryRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/history/HistoryRepository.kt
index 54d24f3..b2e798b 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/history/HistoryRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/history/HistoryRepository.kt
@@ -1,38 +1,31 @@
package com.kom.skyfly.data.repository.history
import com.kom.skyfly.data.datasource.history.HistoryDataSource
-import com.kom.skyfly.data.model.history.SectionedDate
+import com.kom.skyfly.data.mapper.toHistoryDomain
+import com.kom.skyfly.data.model.history.new.HistoryDomain
import com.kom.skyfly.utils.ResultWrapper
-import kotlinx.coroutines.delay
+import com.kom.skyfly.utils.proceedFlow
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
interface HistoryRepository {
- fun getHistoryData(): Flow>>
-
-// fun getDetailHistoryById(id: Int): Flow>
+ fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ): Flow>
}
-class HistoryRepositoryImpl(private val historyDataSource: HistoryDataSource) :
- HistoryRepository {
- override fun getHistoryData(): Flow>> {
- return flow {
- emit(ResultWrapper.Loading())
- delay(2000)
- val result = historyDataSource.getHistoryData()
- emit(ResultWrapper.Success(result))
+class HistoryRepositoryImpl(private val historyDataSource: HistoryDataSource) : HistoryRepository {
+ override fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ): Flow> {
+ return proceedFlow {
+ historyDataSource.getHistoryData(limit, startDate, endDate, flightCode)
+ .toHistoryDomain()
}
}
-
-// override fun getDetailHistoryById(id: Int): Flow> {
-// return flow {
-// emit(ResultWrapper.Loading())
-// val historyDetail = historyDataSource.getHistoryDetailById(id.toString())
-// if (historyDetail != null) {
-// emit(ResultWrapper.Success(historyDetail))
-// } else {
-// emit(ResultWrapper.Error(Exception("History detail not found")))
-// }
-// }
-// }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepository.kt
index dec666a..dc8607f 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepository.kt
@@ -1,9 +1,18 @@
package com.kom.skyfly.data.repository.home.airport
import com.kom.skyfly.data.model.home.search.Airport
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
import com.kom.skyfly.utils.ResultWrapper
import kotlinx.coroutines.flow.Flow
interface AirportRepository {
- fun getAllAirportData(): Flow>>
+ fun getAllAirportData(city: String? = null): Flow>>
+
+ fun createSearchDestinationHistory(searchDestinationHistory: String): Flow>
+
+ fun deleteSearchDestinationHistory(item: SearchDestinationHistory): Flow>
+
+ fun deleteAllSearchDestinationHistory(): Flow>
+
+ fun getUserSearchDestinationHistory(): Flow>>
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepositoryImpl.kt b/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepositoryImpl.kt
index 0070f00..96adb09 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepositoryImpl.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/home/airport/AirportRepositoryImpl.kt
@@ -1,21 +1,65 @@
package com.kom.skyfly.data.repository.home.airport
import com.kom.skyfly.data.datasource.home.HomeDataSource
+import com.kom.skyfly.data.datasource.searchhistory.SearchHistoryDataSource
import com.kom.skyfly.data.mapper.home.toAirports
+import com.kom.skyfly.data.mapper.toSearchDestinationHistoryEntity
+import com.kom.skyfly.data.mapper.toSearchDestinationHistoryList
import com.kom.skyfly.data.model.home.search.Airport
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
+import com.kom.skyfly.data.source.local.database.entity.SearchDestinationHistoryEntity
import com.kom.skyfly.utils.ResultWrapper
+import com.kom.skyfly.utils.proceedFlow
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
class AirportRepositoryImpl(
private val dataSource: HomeDataSource,
+ private val searchHistoryDataSource: SearchHistoryDataSource,
) : AirportRepository {
- override fun getAllAirportData(): Flow>> {
+ override fun getAllAirportData(city: String?): Flow>> {
return flow {
emit(ResultWrapper.Loading())
- val result = dataSource.getAllAirports().data.toAirports()
+ val result = dataSource.getAllAirports(city = city).data.toAirports()
// delay(500)
emit(ResultWrapper.Success(result))
}
}
+
+ override fun createSearchDestinationHistory(searchDestinationHistory: String): Flow> {
+ return proceedFlow {
+ val affectedRows =
+ searchHistoryDataSource.insertSearchDestinationHistory(
+ SearchDestinationHistoryEntity(
+ searchDestinationHistory = searchDestinationHistory,
+ ),
+ )
+ affectedRows > 0
+ }
+ }
+
+ override fun deleteSearchDestinationHistory(item: SearchDestinationHistory): Flow> {
+ return proceedFlow { searchHistoryDataSource.deleteSearchDestinationHistory(item.toSearchDestinationHistoryEntity()) > 0 }
+ }
+
+ override fun deleteAllSearchDestinationHistory(): Flow> {
+ return proceedFlow { searchHistoryDataSource.deleteAllSearchDestinationHistory() }
+ }
+
+ override fun getUserSearchDestinationHistory(): Flow>> {
+ return searchHistoryDataSource.getAllSearchDestinationHistory()
+ .map { searchHistoryEntities ->
+ val searchHistoryList = searchHistoryEntities.toSearchDestinationHistoryList() // Handle nullable case
+ if (searchHistoryList != null) {
+ ResultWrapper.Success(searchHistoryList)
+ } else {
+ ResultWrapper.Empty()
+ }
+ }.onStart {
+ }.catch { e ->
+ }
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/home/destination_favourite/DestinationFavouriteRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/home/destination_favourite/DestinationFavouriteRepository.kt
new file mode 100644
index 0000000..f88496e
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/repository/home/destination_favourite/DestinationFavouriteRepository.kt
@@ -0,0 +1,30 @@
+package com.kom.skyfly.data.repository.home.destination_favourite
+
+import com.kom.skyfly.data.datasource.home.HomeDataSource
+import com.kom.skyfly.data.mapper.home.toDestinationFavourites
+import com.kom.skyfly.data.model.home.destination_favourite.DestinationFavourite
+import com.kom.skyfly.utils.ResultWrapper
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+
+/**
+Written by Komang Yuda Saputra
+Github : https://github.com/YudaSaputraa
+ **/
+interface DestinationFavouriteRepository {
+ fun getAllDestinationFavourite(): Flow>>
+}
+
+class DestinationFavouriteRepositoryImpl(private val destinationFavouriteDataSource: HomeDataSource) :
+ DestinationFavouriteRepository {
+ override fun getAllDestinationFavourite(): Flow>> {
+ return flow {
+ emit(ResultWrapper.Loading())
+ delay(1000)
+ val result =
+ destinationFavouriteDataSource.getDestinationFavourites().data.toDestinationFavourites()
+ emit(ResultWrapper.Success(result))
+ }
+ }
+}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepository.kt
index b6c9000..999a5f6 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepository.kt
@@ -2,6 +2,7 @@ package com.kom.skyfly.data.repository.home.flight_ticket
import com.kom.skyfly.data.model.home.flight.FlightTicket
import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
import com.kom.skyfly.utils.ResultWrapper
import kotlinx.coroutines.flow.Flow
@@ -12,11 +13,34 @@ interface FlightTicketRepository {
departureAirport: String,
arrivalAirport: String,
departureDate: String,
- seatClass: String,
+ seatClass: String?,
+ limit: Int? = 20,
+ returnDate: String? = null,
+ arrivalDate: String? = null,
+ adult: Int? = 1,
+ children: Int? = 0,
+ baby: Int? = 0,
+ ): Flow>>
+
+ fun getReturnTicket(
+ search: String? = null,
+ page: Int? = null,
+ departureAirport: String? = null,
+ arrivalAirport: String? = null,
+ departureDate: String? = null,
+ seatClass: String? = null,
+ limit: Int? = 10000,
+ returnDate: String? = null,
+ arrivalDate: String? = null,
+ adult: Int? = 1,
+ children: Int? = 0,
+ baby: Int? = 0,
): Flow>>
fun getDetailTicket(
id: String,
- seatClass: String,
+ seatClass: String?,
): Flow>
+
+ fun getSeatClassTicket(): List
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepositoryImpl.kt b/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepositoryImpl.kt
index 73c55a5..4afc531 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepositoryImpl.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/home/flight_ticket/FlightTicketRepositoryImpl.kt
@@ -5,6 +5,7 @@ import com.kom.skyfly.data.mapper.home.toFlightDetailTicket
import com.kom.skyfly.data.mapper.home.toFlightTickets
import com.kom.skyfly.data.model.home.flight.FlightTicket
import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
import com.kom.skyfly.utils.ResultWrapper
import com.kom.skyfly.utils.proceedFlow
import kotlinx.coroutines.flow.Flow
@@ -18,22 +19,65 @@ class FlightTicketRepositoryImpl(
departureAirport: String,
arrivalAirport: String,
departureDate: String,
- seatClass: String,
+ seatClass: String?,
+ limit: Int?,
+ returnDate: String?,
+ arrivalDate: String?,
+ adult: Int?,
+ children: Int?,
+ baby: Int?,
): Flow>> {
return proceedFlow {
dataSource.getAllFlight(
page = page,
departureAirport = departureAirport,
+ limit = limit,
arrivalAirport = arrivalAirport,
departureDate = departureDate,
seatClass = seatClass,
+ returnDate = returnDate,
+ adult = adult,
+ baby = baby,
+ children = children,
).data.toFlightTickets()
}
}
+ override fun getReturnTicket(
+ search: String?,
+ page: Int?,
+ departureAirport: String?,
+ arrivalAirport: String?,
+ departureDate: String?,
+ seatClass: String?,
+ limit: Int?,
+ returnDate: String?,
+ arrivalDate: String?,
+ adult: Int?,
+ children: Int?,
+ baby: Int?,
+ ): Flow>> {
+ return proceedFlow {
+ dataSource.getAllFlight(
+ page = page ?: 0,
+ departureAirport = departureAirport.orEmpty(),
+ limit = limit,
+ arrivalAirport = arrivalAirport!!,
+ departureDate = departureDate.orEmpty(),
+ seatClass = seatClass,
+ returnDate = returnDate,
+ adult = adult,
+ baby = baby,
+ children = children,
+ ).returnFlight.toFlightTickets()
+ }
+ }
+
+ override fun getSeatClassTicket(): List = dataSource.getSeatClassData()
+
override fun getDetailTicket(
id: String,
- seatClass: String,
+ seatClass: String?,
): Flow> {
return proceedFlow {
dataSource.getDetailFlight(
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/notification/NotificationRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/notification/NotificationRepository.kt
index 993c3dc..67f0a8c 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/notification/NotificationRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/notification/NotificationRepository.kt
@@ -4,9 +4,8 @@ import com.kom.skyfly.data.datasource.notification.NotificationDataSource
import com.kom.skyfly.data.mapper.toNotifications
import com.kom.skyfly.data.model.notification.Notification
import com.kom.skyfly.utils.ResultWrapper
-import kotlinx.coroutines.delay
+import com.kom.skyfly.utils.proceedFlow
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
/**
Written by Komang Yuda Saputra
@@ -19,11 +18,8 @@ interface NotificationRepository {
class NotificationRepositoryImpl(private val notificationDataSource: NotificationDataSource) :
NotificationRepository {
override fun getAllNotification(): Flow>> {
- return flow {
- emit(ResultWrapper.Loading())
- delay(1000)
- val result = notificationDataSource.getAllNotification().data.toNotifications()
- emit(ResultWrapper.Success(result))
+ return proceedFlow {
+ notificationDataSource.getAllNotification().data.toNotifications()
}
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/profiles/ProfileRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/profiles/ProfileRepository.kt
index fe36b96..f3ee204 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/profiles/ProfileRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/profiles/ProfileRepository.kt
@@ -18,6 +18,7 @@ interface ProfileRepository {
fun updateProfile(
name: String?,
phoneNumber: String?,
+ familyName: String?,
password: String?,
confirmPassword: String?,
): Flow>
@@ -31,9 +32,10 @@ class ProfileRepositoryImpl(private val dataSource: ProfileDataSource) : Profile
override fun updateProfile(
name: String?,
phoneNumber: String?,
+ familyName: String?,
password: String?,
confirmPassword: String?,
): Flow> {
- return proceedFlow { dataSource.updateUserProfile(name, phoneNumber, password, confirmPassword) }
+ return proceedFlow { dataSource.updateUserProfile(name, phoneNumber, familyName, password, confirmPassword) }
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepository.kt
index 0039eb1..3c02ad0 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepository.kt
@@ -58,14 +58,17 @@ class SearchHistoryRepositoryImpl(private val searchHistoryDataSource: SearchHis
override fun getUserSearchHistory(): Flow>> {
return searchHistoryDataSource.getAllSearchHistory()
.map { searchHistoryEntities ->
- val searchHistoryList = searchHistoryEntities.toSearchHistoryList() // Handle nullable case
+ val searchHistoryList =
+ searchHistoryEntities.toSearchHistoryList()
if (searchHistoryList != null) {
ResultWrapper.Success(searchHistoryList)
} else {
ResultWrapper.Empty()
}
- }.onStart {
}.catch { e ->
+ emit(ResultWrapper.Error(Exception(e)))
+ }.onStart {
+ emit(ResultWrapper.Loading())
}
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/repository/transaction/TransactionRepository.kt b/app/src/main/java/com/kom/skyfly/data/repository/transaction/TransactionRepository.kt
index e3ed711..eb2f9cb 100644
--- a/app/src/main/java/com/kom/skyfly/data/repository/transaction/TransactionRepository.kt
+++ b/app/src/main/java/com/kom/skyfly/data/repository/transaction/TransactionRepository.kt
@@ -1,8 +1,12 @@
package com.kom.skyfly.data.repository.transaction
import com.kom.skyfly.data.datasource.transaction.TransactionDataSource
+import com.kom.skyfly.data.mapper.toCancelTransaction
+import com.kom.skyfly.data.mapper.toItemsPaymentStatusDomain
import com.kom.skyfly.data.mapper.toTransactionDetailResponse
+import com.kom.skyfly.data.model.transaction.cancel.ItemsCancelTransactionModel
import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
+import com.kom.skyfly.data.model.transaction.paymentstatus.ItemsPaymentStatus
import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
import com.kom.skyfly.data.source.network.model.transaction.response.TransactionResponse
import com.kom.skyfly.utils.ResultWrapper
@@ -23,6 +27,10 @@ interface TransactionRepository {
): Flow>
fun getTransactionById(id: String): Flow>
+
+ fun getPaymentStatus(id: String): Flow>
+
+ fun cancelTransaction(id: String): Flow>
}
class TransactionRepositoryImpl(private val datasource: TransactionDataSource) :
@@ -47,7 +55,20 @@ class TransactionRepositoryImpl(private val datasource: TransactionDataSource) :
override fun getTransactionById(id: String): Flow> {
return proceedFlow {
- datasource.getTransactionById(id).toTransactionDetailResponse()
+ datasource.getTransactionById(id)
+ .toTransactionDetailResponse()
+ }
+ }
+
+ override fun getPaymentStatus(id: String): Flow> {
+ return proceedFlow {
+ datasource.getPaymentStatus(id).data.toItemsPaymentStatusDomain()
+ }
+ }
+
+ override fun cancelTransaction(id: String): Flow> {
+ return proceedFlow {
+ datasource.cancelTransaction(id).data.toCancelTransaction()
}
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/source/local/database/AppDatabase.kt b/app/src/main/java/com/kom/skyfly/data/source/local/database/AppDatabase.kt
index 32a1489..6d14e8d 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/local/database/AppDatabase.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/local/database/AppDatabase.kt
@@ -14,7 +14,7 @@ Github : https://github.com/YudaSaputraa
**/
@Database(
entities = [SearchHistoryEntity::class, SearchDestinationHistoryEntity::class],
- version = 2,
+ version = 3,
exportSchema = true,
)
abstract class AppDatabase : RoomDatabase() {
diff --git a/app/src/main/java/com/kom/skyfly/data/source/local/database/dao/SearchHistoryDao.kt b/app/src/main/java/com/kom/skyfly/data/source/local/database/dao/SearchHistoryDao.kt
index bbcce07..a16880e 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/local/database/dao/SearchHistoryDao.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/local/database/dao/SearchHistoryDao.kt
@@ -25,12 +25,24 @@ interface SearchHistoryDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSearchHistory(searchHistory: SearchHistoryEntity): Long
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Long
+
@Update
suspend fun updateSearchHistory(searchHistory: SearchHistoryEntity): Int
+ @Update
+ suspend fun updateSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int
+
@Delete
suspend fun deleteSearchHistory(searchHistory: SearchHistoryEntity): Int
+ @Delete
+ suspend fun deleteSearchDestinationHistory(searchDestinationHistory: SearchDestinationHistoryEntity): Int
+
@Query("DELETE FROM search_history")
suspend fun deleteAll()
+
+ @Query("DELETE FROM search_destination_history")
+ suspend fun deleteAllRecentSearchDestination()
}
diff --git a/app/src/main/java/com/kom/skyfly/data/source/local/database/entity/SearchDestinationHistoryEntity.kt b/app/src/main/java/com/kom/skyfly/data/source/local/database/entity/SearchDestinationHistoryEntity.kt
index 4ab22fe..32e7034 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/local/database/entity/SearchDestinationHistoryEntity.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/local/database/entity/SearchDestinationHistoryEntity.kt
@@ -9,5 +9,5 @@ data class SearchDestinationHistoryEntity(
@PrimaryKey(autoGenerate = true)
var id: Int? = null,
@ColumnInfo(name = "search_history")
- var searchHistory: String,
+ var searchDestinationHistory: String,
)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/local/pref/UserPreference.kt b/app/src/main/java/com/kom/skyfly/data/source/local/pref/UserPreference.kt
index 15152a9..5214948 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/local/pref/UserPreference.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/local/pref/UserPreference.kt
@@ -1,6 +1,7 @@
package com.kom.skyfly.data.source.local.pref
import android.content.SharedPreferences
+import com.kom.skyfly.utils.SharedPreferenceUtils.get
import com.kom.skyfly.utils.SharedPreferenceUtils.set
/**
@@ -42,5 +43,6 @@ class UserPreferenceImpl(private val pref: SharedPreferences) : UserPreference {
const val PREF_NAME = "skyfly-pref"
const val KEY_ON_BOARDING_SHOW = "KEY_ON_BOARDING_SHOW"
const val KEY_TOKEN = "KEY_TOKEN"
+ const val KEY_PAYMENT_URL = "KEY_PAYMENT_URL"
}
}
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Airline.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Airline.kt
new file mode 100644
index 0000000..cb8b282
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Airline.kt
@@ -0,0 +1,18 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Airline(
+ @SerializedName("code")
+ val code: String?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("image")
+ val image: String?,
+ @SerializedName("name")
+ val name: String?,
+ @SerializedName("terminal")
+ val terminal: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Arrival.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Arrival.kt
new file mode 100644
index 0000000..abc3d47
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Arrival.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Arrival(
+ @SerializedName("date")
+ val date: String?,
+ @SerializedName("time")
+ val time: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Booking.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Booking.kt
new file mode 100644
index 0000000..80762f4
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Booking.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Booking(
+ @SerializedName("code")
+ val code: String?,
+ @SerializedName("date")
+ val date: String?,
+ @SerializedName("time")
+ val time: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Departure.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Departure.kt
new file mode 100644
index 0000000..fcfda26
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Departure.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Departure(
+ @SerializedName("date")
+ val date: String?,
+ @SerializedName("time")
+ val time: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DepartureAirport.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DepartureAirport.kt
new file mode 100644
index 0000000..f5a6bde
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DepartureAirport.kt
@@ -0,0 +1,22 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class DepartureAirport(
+ @SerializedName("city")
+ val city: String?,
+ @SerializedName("code")
+ val code: String?,
+ @SerializedName("continent")
+ val continent: String?,
+ @SerializedName("country")
+ val country: String?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("image")
+ val image: String?,
+ @SerializedName("name")
+ val name: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DestinationAirport.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DestinationAirport.kt
new file mode 100644
index 0000000..af8f588
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/DestinationAirport.kt
@@ -0,0 +1,22 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class DestinationAirport(
+ @SerializedName("city")
+ val city: String?,
+ @SerializedName("code")
+ val code: String?,
+ @SerializedName("continent")
+ val continent: String?,
+ @SerializedName("country")
+ val country: String?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("image")
+ val image: String?,
+ @SerializedName("name")
+ val name: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Flight.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Flight.kt
new file mode 100644
index 0000000..96df796
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Flight.kt
@@ -0,0 +1,26 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Flight(
+ @SerializedName("airline")
+ val airline: Airline?,
+ @SerializedName("arrival")
+ val arrival: Arrival?,
+ @SerializedName("code")
+ val code: String?,
+ @SerializedName("departure")
+ val departure: Departure?,
+ @SerializedName("departureAirport")
+ val departureAirport: DepartureAirport?,
+ @SerializedName("destinationAirport")
+ val destinationAirport: DestinationAirport?,
+ @SerializedName("flightDuration")
+ val flightDuration: String?,
+ @SerializedName("flightPrice")
+ val flightPrice: Int?,
+ @SerializedName("id")
+ val id: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/HistoryResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/HistoryResponse.kt
new file mode 100644
index 0000000..2517ba1
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/HistoryResponse.kt
@@ -0,0 +1,16 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class HistoryResponse(
+ @SerializedName("data")
+ val data: List?,
+ @SerializedName("message")
+ val message: String?,
+ @SerializedName("status")
+ val status: Boolean?,
+ @SerializedName("totalItems")
+ val totalItems: Int?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/ItemsHistoryResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/ItemsHistoryResponse.kt
new file mode 100644
index 0000000..c723bd9
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/ItemsHistoryResponse.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class ItemsHistoryResponse(
+ @SerializedName("date")
+ val date: String?,
+ @SerializedName("transactions")
+ val transactions: List?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Pagination.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Pagination.kt
new file mode 100644
index 0000000..730b82b
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Pagination.kt
@@ -0,0 +1,18 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Pagination(
+ @SerializedName("currentPage")
+ val currentPage: Int?,
+ @SerializedName("nextPage")
+ val nextPage: Int?,
+ @SerializedName("pageItems")
+ val pageItems: Int?,
+ @SerializedName("prevPage")
+ val prevPage: Int?,
+ @SerializedName("totalPage")
+ val totalPage: Int?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Seat.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Seat.kt
new file mode 100644
index 0000000..b5be600
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Seat.kt
@@ -0,0 +1,20 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Seat(
+ @SerializedName("flightId")
+ val flightId: String?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("seatNumber")
+ val seatNumber: String?,
+ @SerializedName("seatPrice")
+ val seatPrice: Int?,
+ @SerializedName("status")
+ val status: String?,
+ @SerializedName("type")
+ val type: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Transaction.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Transaction.kt
new file mode 100644
index 0000000..8cc3a97
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/Transaction.kt
@@ -0,0 +1,24 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Transaction(
+ @SerializedName("booking")
+ val booking: Booking?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("orderId")
+ val orderId: String?,
+ @SerializedName("status")
+ val status: String?,
+ @SerializedName("tax")
+ val tax: Int?,
+ @SerializedName("totalPrice")
+ val totalPrice: Int?,
+ @SerializedName("Transaction_Detail")
+ val transactionDetail: List?,
+ @SerializedName("userId")
+ val userId: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/history/TransactionDetail.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/TransactionDetail.kt
new file mode 100644
index 0000000..f68cded
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/history/TransactionDetail.kt
@@ -0,0 +1,34 @@
+package com.kom.skyfly.data.source.network.model.history
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class TransactionDetail(
+ @SerializedName("citizenship")
+ val citizenship: String?,
+ @SerializedName("dob")
+ val dob: String?,
+ @SerializedName("familyName")
+ val familyName: String?,
+ @SerializedName("flight")
+ val flight: Flight?,
+ @SerializedName("id")
+ val id: String?,
+ @SerializedName("issuingCountry")
+ val issuingCountry: String?,
+ @SerializedName("name")
+ val name: String?,
+ @SerializedName("passengerCategory")
+ val passengerCategory: String?,
+ @SerializedName("passport")
+ val passport: String?,
+ @SerializedName("seat")
+ val seat: Seat?,
+ @SerializedName("totalPrice")
+ val totalPrice: Int?,
+ @SerializedName("transactionId")
+ val transactionId: String?,
+ @SerializedName("validityPeriod")
+ val validityPeriod: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/ArrivalDestination.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/ArrivalDestination.kt
new file mode 100644
index 0000000..1c2d5c4
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/ArrivalDestination.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import com.google.gson.annotations.SerializedName
+
+data class ArrivalDestination(
+ @SerializedName("arrivalDate")
+ val arrivalDate: String,
+ @SerializedName("arrivalCity")
+ val arrivalCity: String,
+ @SerializedName("image")
+ val image: String,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationData.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationData.kt
new file mode 100644
index 0000000..df738b4
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationData.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import com.google.gson.annotations.SerializedName
+
+data class FavouriteDestinationData(
+ @SerializedName("flightId")
+ val flightId: String,
+ @SerializedName("from")
+ val sourceDestination: SourceDestination,
+ @SerializedName("to")
+ val arrivalDestination: ArrivalDestination,
+ @SerializedName("plane")
+ val plane: Plane,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationResponse.kt
new file mode 100644
index 0000000..579812b
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FavouriteDestinationResponse.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class FavouriteDestinationResponse(
+ @SerializedName("status")
+ val status: Boolean,
+ @SerializedName("message")
+ val message: String,
+ @SerializedName("data")
+ val data: List,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FlightDetailResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FlightDetailResponse.kt
new file mode 100644
index 0000000..d27b29b
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/FlightDetailResponse.kt
@@ -0,0 +1,10 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class FlightDetailResponse(
+ @SerializedName("flightDetails")
+ val flightDetails: FavouriteDestinationData,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/Plane.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/Plane.kt
new file mode 100644
index 0000000..a1a2ae9
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/Plane.kt
@@ -0,0 +1,10 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import com.google.gson.annotations.SerializedName
+
+data class Plane(
+ @SerializedName("airline")
+ val airline: String,
+ @SerializedName("price")
+ val price: Int,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/SourceDestination.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/SourceDestination.kt
new file mode 100644
index 0000000..bdc52cf
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/favourite_destination/SourceDestination.kt
@@ -0,0 +1,10 @@
+package com.kom.skyfly.data.source.network.model.home.favourite_destination
+
+import com.google.gson.annotations.SerializedName
+
+data class SourceDestination(
+ @SerializedName("departureDate")
+ val departureDate: String,
+ @SerializedName("departureCity")
+ val departureCity: String,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/FlightResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/FlightResponse.kt
index 5f31c17..1b9281f 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/FlightResponse.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/FlightResponse.kt
@@ -19,4 +19,6 @@ data class FlightResponse(
val priceRange: PriceRange,
@SerializedName("data")
val data: List,
+ @SerializedName("returnFlights")
+ val returnFlight: List,
)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/PriceRange.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/PriceRange.kt
index ce7fb67..72046ae 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/PriceRange.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/home/flight/PriceRange.kt
@@ -10,4 +10,5 @@ data class PriceRange(
val economyClass: Price,
@SerializedName("BUSINESS")
val businessClass: Price,
+ val name: String? = "Business",
)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/ItemsPaymentStatusResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/ItemsPaymentStatusResponse.kt
new file mode 100644
index 0000000..d710f3a
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/ItemsPaymentStatusResponse.kt
@@ -0,0 +1,32 @@
+package com.kom.skyfly.data.source.network.model.paymentstatus
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class ItemsPaymentStatusResponse(
+ @SerializedName("currency")
+ val currency: String?,
+ @SerializedName("expiry_time")
+ val expiryTime: String?,
+ @SerializedName("gross_amount")
+ val grossAmount: String?,
+ @SerializedName("merchant_id")
+ val merchantId: String?,
+ @SerializedName("order_id")
+ val orderId: String?,
+ @SerializedName("payment_status")
+ val paymentStatus: String?,
+ @SerializedName("payment_type")
+ val paymentType: String?,
+ @SerializedName("signature_key")
+ val signatureKey: String?,
+ @SerializedName("transaction_id")
+ val transactionId: String?,
+ @SerializedName("transaction_status")
+ val transactionStatus: String?,
+ @SerializedName("transaction_time")
+ val transactionTime: String?,
+ @SerializedName("va_numbers")
+ val vaNumbers: List?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/PaymentStatusResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/PaymentStatusResponse.kt
new file mode 100644
index 0000000..3d55ed2
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/PaymentStatusResponse.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.source.network.model.paymentstatus
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class PaymentStatusResponse(
+ @SerializedName("data")
+ val data: ItemsPaymentStatusResponse?,
+ @SerializedName("message")
+ val message: String?,
+ @SerializedName("status")
+ val status: Boolean?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/VaNumber.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/VaNumber.kt
new file mode 100644
index 0000000..2b280d2
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/paymentstatus/VaNumber.kt
@@ -0,0 +1,12 @@
+package com.kom.skyfly.data.source.network.model.paymentstatus
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class VaNumber(
+ @SerializedName("bank")
+ val bank: String?,
+ @SerializedName("va_number")
+ val vaNumber: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/resendotp/ResendOtpSmsRequest.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/resendotp/ResendOtpSmsRequest.kt
new file mode 100644
index 0000000..b160234
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/resendotp/ResendOtpSmsRequest.kt
@@ -0,0 +1,11 @@
+package com.kom.skyfly.data.source.network.model.resendotp
+
+import com.google.gson.annotations.SerializedName
+
+/**
+Written by Komang Yuda Saputra
+Github : https://github.com/YudaSaputraa
+ **/
+data class ResendOtpSmsRequest(
+ @SerializedName("phoneNumber") val phoneNumber: String,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/CancelTransactionResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/CancelTransactionResponse.kt
new file mode 100644
index 0000000..e7a22b0
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/CancelTransactionResponse.kt
@@ -0,0 +1,14 @@
+package com.kom.skyfly.data.source.network.model.transaction.cancel
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class CancelTransactionResponse(
+ @SerializedName("data")
+ val data: ItemsCancelTransactionResponse?,
+ @SerializedName("message")
+ val message: String?,
+ @SerializedName("status")
+ val status: Boolean?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/ItemsCancelTransactionResponse.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/ItemsCancelTransactionResponse.kt
new file mode 100644
index 0000000..6917d47
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/transaction/cancel/ItemsCancelTransactionResponse.kt
@@ -0,0 +1,26 @@
+package com.kom.skyfly.data.source.network.model.transaction.cancel
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class ItemsCancelTransactionResponse(
+ @SerializedName("currency")
+ val currency: String?,
+ @SerializedName("gross_amount")
+ val grossAmount: String?,
+ @SerializedName("merchant_id")
+ val merchantId: String?,
+ @SerializedName("order_id")
+ val orderId: String?,
+ @SerializedName("payment_status")
+ val paymentStatus: String?,
+ @SerializedName("payment_type")
+ val paymentType: String?,
+ @SerializedName("transaction_id")
+ val transactionId: String?,
+ @SerializedName("transaction_status")
+ val transactionStatus: String?,
+ @SerializedName("transaction_time")
+ val transactionTime: String?,
+)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/model/userprofile/updateprofile/UpdateProfileRequest.kt b/app/src/main/java/com/kom/skyfly/data/source/network/model/userprofile/updateprofile/UpdateProfileRequest.kt
index 05b3509..1332223 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/network/model/userprofile/updateprofile/UpdateProfileRequest.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/model/userprofile/updateprofile/UpdateProfileRequest.kt
@@ -9,6 +9,7 @@ Github : https://github.com/YudaSaputraa
data class UpdateProfileRequest(
@SerializedName("name") val name: String?,
@SerializedName("phoneNumber") val phoneNumber: String?,
+ @SerializedName("familyName") val familyName: String?,
@SerializedName("password") val password: String?,
@SerializedName("confirmPassword") val confirmPassword: String?,
)
diff --git a/app/src/main/java/com/kom/skyfly/data/source/network/services/SkyFlyApiService.kt b/app/src/main/java/com/kom/skyfly/data/source/network/services/SkyFlyApiService.kt
index 8d90f5e..1798404 100644
--- a/app/src/main/java/com/kom/skyfly/data/source/network/services/SkyFlyApiService.kt
+++ b/app/src/main/java/com/kom/skyfly/data/source/network/services/SkyFlyApiService.kt
@@ -7,15 +7,20 @@ import com.kom.skyfly.data.source.local.pref.UserPreference
import com.kom.skyfly.data.source.network.model.flightseat.FlightSeatResponse
import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordRequest
import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordResponse
+import com.kom.skyfly.data.source.network.model.history.HistoryResponse
import com.kom.skyfly.data.source.network.model.home.airport.AirportResponse
+import com.kom.skyfly.data.source.network.model.home.favourite_destination.FavouriteDestinationResponse
import com.kom.skyfly.data.source.network.model.home.flight.FlightResponse
import com.kom.skyfly.data.source.network.model.home.flight_detail.FlightDetailResponse
import com.kom.skyfly.data.source.network.model.login.LoginRequest
import com.kom.skyfly.data.source.network.model.login.LoginResponse
import com.kom.skyfly.data.source.network.model.notification.NotificationResponse
+import com.kom.skyfly.data.source.network.model.paymentstatus.PaymentStatusResponse
import com.kom.skyfly.data.source.network.model.register.RegisterRequest
import com.kom.skyfly.data.source.network.model.register.RegisterResponse
import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpResponse
+import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpSmsRequest
+import com.kom.skyfly.data.source.network.model.transaction.cancel.CancelTransactionResponse
import com.kom.skyfly.data.source.network.model.transaction.detail.TransactionDetailResponse
import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
import com.kom.skyfly.data.source.network.model.transaction.response.TransactionResponse
@@ -72,22 +77,29 @@ interface SkyFlyApiService {
@GET("api/v1/flights/")
suspend fun getAllFlights(
@Query("search") search: String? = null,
- @Query("page") page: Int,
- @Query("departureAirport") departureAirport: String,
- @Query("arrivalAirport") arrivalAirport: String,
- @Query("departureDate") departureDate: String,
+ @Query("page") page: Int?,
+ @Query("limit") limit: Int? = 10000,
+ @Query("departureAirport") departureAirport: String?,
+ @Query("arrivalAirport") arrivalAirport: String?,
+ @Query("departureDate") departureDate: String?,
+ @Query("returnDate") returnDate: String?,
@Query("arrivalDate") arrivalDate: String? = null,
- @Query("seatClass") seatClass: String,
+ @Query("seatClass") seatClass: String?,
+ @Query("adult") adult: Int? = 0,
+ @Query("children") children: Int? = 0,
+ @Query("baby") baby: Int? = 0,
+ @Query("sort") sort: String? = null,
): FlightResponse
@GET("api/v1/flights/{id}")
suspend fun getDetailFlightById(
@Path("id") id: String,
- @Query("seatClass") seatClass: String,
+ @Query("seatClass") seatClass: String?,
): FlightDetailResponse
@GET("api/v1/airports/")
suspend fun getAllAirports(
+ @Query("city") city: String?,
@Query("showall") showAll: Boolean = true,
): AirportResponse
@@ -97,6 +109,9 @@ interface SkyFlyApiService {
@Query("limit") limit: Int? = 5000,
): FlightSeatResponse
+ @GET("api/v1/flights/favorite-destination")
+ suspend fun getDestinationFavourite(): FavouriteDestinationResponse
+
@GET("api/v1/auth/me")
suspend fun getUserProfile(): UserProfileResponse
@@ -124,6 +139,30 @@ interface SkyFlyApiService {
@Path("id") id: String,
): TransactionDetailResponse
+ @POST("api/v1/auth/verified/resendSMS-otp")
+ suspend fun resendOtpSms(
+ @Query("token") token: String,
+ @Body resendOtpRequest: ResendOtpSmsRequest,
+ ): ResendOtpResponse
+
+ @GET("api/v1/transactions")
+ suspend fun getAllTransactionHistory(
+ @Query("limit") limit: Int?,
+ @Query("startDate") startDate: String?,
+ @Query("endDate") endDate: String?,
+ @Query("flightCode") flightCode: String?,
+ ): HistoryResponse
+
+ @GET("api/v1/transactions/status/{id}")
+ suspend fun getPaymentStatus(
+ @Path("id") id: String,
+ ): PaymentStatusResponse
+
+ @POST("api/v1/transactions/cancel/{id}")
+ suspend fun cancelTransaction(
+ @Path("id") id: String,
+ ): CancelTransactionResponse
+
companion object {
@JvmStatic
operator fun invoke(
@@ -141,7 +180,7 @@ interface SkyFlyApiService {
.addInterceptor { chain ->
val original: Request = chain.request()
val token = userPreference.getUserToken()
-// val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUxOTM5OGQ4LTI3YTUtNDRkMi1iMzYxLTVjNDI2NjU0YjBiOCIsIm5hbWUiOiJrb21hbmd5dWRhIiwiZW1haWwiOiJ5dWRhc2FwdXRyYTA4MkBnbWFpbC5jb20iLCJwaG9uZU51bWJlciI6IjYyODc0Njc0NjQ3ODciLCJpYXQiOjE3MTgwMzQzMzksImV4cCI6MTcxODEyMDczOX0.r_vTbQwhr3NMZdGzfGyveF6rE-E1LOCC0BGv45dhNAw"
+ // val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUxOTM5OGQ4LTI3YTUtNDRkMi1iMzYxLTVjNDI2NjU0YjBiOCIsIm5hbWUiOiJrb21hbmd5dWRhIiwiZW1haWwiOiJ5dWRhc2FwdXRyYTA4MkBnbWFpbC5jb20iLCJwaG9uZU51bWJlciI6IjYyODc0Njc0NjQ3ODciLCJpYXQiOjE3MTgwMzQzMzksImV4cCI6MTcxODEyMDczOX0.r_vTbQwhr3NMZdGzfGyveF6rE-E1LOCC0BGv45dhNAw"
val requestBuilder: Request.Builder =
original.newBuilder()
.addHeader("accept", "application/json")
diff --git a/app/src/main/java/com/kom/skyfly/di/AppModules.kt b/app/src/main/java/com/kom/skyfly/di/AppModules.kt
index 9d1f0ee..d322e9e 100644
--- a/app/src/main/java/com/kom/skyfly/di/AppModules.kt
+++ b/app/src/main/java/com/kom/skyfly/di/AppModules.kt
@@ -1,13 +1,10 @@
package com.kom.skyfly.di
import android.content.SharedPreferences
-import android.os.Bundle
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.kom.skyfly.core.BaseViewModel
import com.kom.skyfly.data.datasource.auth.AuthDataSource
import com.kom.skyfly.data.datasource.auth.AuthDataSourceImpl
-import com.kom.skyfly.data.datasource.destinationfavorite.DestinationFavoriteDataSource
-import com.kom.skyfly.data.datasource.destinationfavorite.DestinationFavoriteDataSourceImpl
import com.kom.skyfly.data.datasource.flightseat.FlightSeatDataSource
import com.kom.skyfly.data.datasource.flightseat.FlightSeatDataSourceImpl
import com.kom.skyfly.data.datasource.history.HistoryDataSource
@@ -26,14 +23,14 @@ import com.kom.skyfly.data.datasource.userpreference.UserPrefDataSource
import com.kom.skyfly.data.datasource.userpreference.UserPrefDataSourceImpl
import com.kom.skyfly.data.repository.auth.AuthRepository
import com.kom.skyfly.data.repository.auth.AuthRepositoryImpl
-import com.kom.skyfly.data.repository.destinationfavorite.DestinationFavoriteRepository
-import com.kom.skyfly.data.repository.destinationfavorite.DestinationFavoriteRepositoryImpl
import com.kom.skyfly.data.repository.flightseat.FlightSeatRepository
import com.kom.skyfly.data.repository.flightseat.FlightSeatRepositoryImpl
import com.kom.skyfly.data.repository.history.HistoryRepository
import com.kom.skyfly.data.repository.history.HistoryRepositoryImpl
import com.kom.skyfly.data.repository.home.airport.AirportRepository
import com.kom.skyfly.data.repository.home.airport.AirportRepositoryImpl
+import com.kom.skyfly.data.repository.home.destination_favourite.DestinationFavouriteRepository
+import com.kom.skyfly.data.repository.home.destination_favourite.DestinationFavouriteRepositoryImpl
import com.kom.skyfly.data.repository.home.flight_ticket.FlightTicketRepository
import com.kom.skyfly.data.repository.home.flight_ticket.FlightTicketRepositoryImpl
import com.kom.skyfly.data.repository.notification.NotificationRepository
@@ -57,7 +54,6 @@ import com.kom.skyfly.presentation.checkout.bookersbiodata.BookersBiodataViewMod
import com.kom.skyfly.presentation.checkout.checkoutticket.CheckoutTicketViewModel
import com.kom.skyfly.presentation.checkout.chooseseat.ChooseSeatViewModel
import com.kom.skyfly.presentation.checkout.flightdetail.FlightDetailViewModel
-import com.kom.skyfly.presentation.checkout.passengerbiodata.PassengerBiodataViewModel
import com.kom.skyfly.presentation.forgetpassword.ForgetPasswordViewModel
import com.kom.skyfly.presentation.history.HistoryViewModel
import com.kom.skyfly.presentation.history.flightdetailhistory.FlightDetailHistoryViewModel
@@ -66,6 +62,7 @@ import com.kom.skyfly.presentation.home.HomeViewModel
import com.kom.skyfly.presentation.home.detail_home.DetailHomeViewModel
import com.kom.skyfly.presentation.home.search.SearchViewModel
import com.kom.skyfly.presentation.home.search_result.SearchResultViewModel
+import com.kom.skyfly.presentation.home.seatclass.SeatClassViewModel
import com.kom.skyfly.presentation.login.LoginViewModel
import com.kom.skyfly.presentation.main.MainViewModel
import com.kom.skyfly.presentation.notification.NotificationViewModel
@@ -74,7 +71,6 @@ import com.kom.skyfly.presentation.register.RegisterViewModel
import com.kom.skyfly.presentation.verifyotp.VerifyOtpViewModel
import com.kom.skyfly.utils.SharedPreferenceUtils
import org.koin.android.ext.koin.androidContext
-import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module
@@ -93,13 +89,12 @@ object AppModules {
module {
single { AuthDataSourceImpl(get()) }
single { UserPrefDataSourceImpl(get()) }
- single { HistoryDataSourceImpl() }
+ single { HistoryDataSourceImpl(get()) }
single { NotificationDataSourceImpl(get()) }
single { ProfileDataSourceImpl(get()) }
single { SearchHistoryDataSourceImpl(get()) }
single { HomeDataSourceImpl(get()) }
single { FlightSeatDataSourceImpl(get()) }
- single { DestinationFavoriteDataSourceImpl() }
single { TransactionDataSourceImpl(get()) }
}
@@ -124,9 +119,9 @@ object AppModules {
single { HistoryRepositoryImpl(get()) }
single { ProfileRepositoryImpl(get()) }
single { SearchHistoryRepositoryImpl(get()) }
- single { AirportRepositoryImpl(get()) }
+ single { AirportRepositoryImpl(get(), get()) }
single { FlightSeatRepositoryImpl(get()) }
- single { DestinationFavoriteRepositoryImpl(get()) }
+ single { DestinationFavouriteRepositoryImpl(get()) }
single { FlightTicketRepositoryImpl(get()) }
single { TransactionRepositoryImpl(get()) }
}
@@ -145,20 +140,15 @@ object AppModules {
viewModelOf(::AccountViewModel)
viewModelOf(::NotificationViewModel)
viewModelOf(::HistoryViewModel)
+ viewModelOf(::SeatClassViewModel)
viewModelOf(::DetailHomeViewModel)
viewModelOf(::SearchFlightHistoryViewModel)
viewModelOf(::BaseViewModel)
viewModelOf(::SearchViewModel)
viewModelOf(::ChooseSeatViewModel)
- viewModel { (extras: Bundle?) ->
- FlightDetailHistoryViewModel(
- extras = extras,
- historyRepository = get(),
- )
- }
+ viewModelOf(::FlightDetailHistoryViewModel)
viewModelOf(::SharedViewModelEditProfile)
viewModelOf(::BookersBiodataViewModel)
- viewModelOf(::PassengerBiodataViewModel)
viewModelOf(::CheckoutTicketViewModel)
viewModelOf(::FlightDetailViewModel)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/account/AccountFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/account/AccountFragment.kt
index 23d5bcd..be00f1d 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/account/AccountFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/account/AccountFragment.kt
@@ -7,8 +7,8 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.findNavController
import com.kom.skyfly.R
import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.databinding.FragmentAccountBinding
@@ -16,6 +16,8 @@ import com.kom.skyfly.presentation.account.editprofile.BottomSheetsChangePasswor
import com.kom.skyfly.presentation.account.editprofile.BottomSheetsEditProfile
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import es.dmoral.toasty.Toasty
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -28,6 +30,8 @@ class AccountFragment : Fragment() {
private var email: String? = null
private var fullName: String? = null
private var phoneNumber: String? = null
+ private var familyName: String? = null
+
private var id: String? = null
override fun onCreateView(
@@ -76,7 +80,27 @@ class AccountFragment : Fragment() {
).show()
},
doOnError = {
- Log.d("req-changePassword", "reqChangePassword: ${it.exception?.message}")
+ if (it.exception is NoInternetException) {
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (it.exception is UnAuthorizeException) {
+ binding.csvProfile.isVisible = true
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ (activity as BaseActivity).errorHandler(it.exception)
+ } else if (it.exception is ServerErrorException) {
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_server_error_please_try_again_later),
+ )
+ (activity as BaseActivity).errorHandler(it.exception)
+ } else {
+ Log.d("req-changePassword", "reqChangePassword: ${it.exception?.message}")
+ }
},
)
}
@@ -84,19 +108,21 @@ class AccountFragment : Fragment() {
private fun setClickListeners() {
binding.layoutBtnProfile.tvEditProfile.setOnClickListener {
- fullName?.let { fullName ->
- phoneNumber?.let { phoneNumber ->
- id?.let { id ->
- doEditProfile(id, fullName, phoneNumber)
+ familyName?.let { familyNames ->
+ fullName?.let { fullName ->
+ phoneNumber?.let { phoneNumber ->
+ id?.let { id ->
+ doEditProfile(id, fullName, phoneNumber, familyNames)
+ Toast.makeText(requireContext(), "tes", Toast.LENGTH_SHORT).show()
+ }
}
}
}
}
binding.layoutBtnProfile.tvLogoutProfile.setOnClickListener {
accountViewModel.doLogout(null)
- (activity as BaseActivity).handleUnAuthorize()
+ (activity as BaseActivity).doLogoutHandler()
Toasty.normal(requireContext(), "Success!", Toast.LENGTH_SHORT).show()
- navigateToHome()
}
binding.layoutBtnProfile.tvChangePassword.setOnClickListener {
doChangePassword()
@@ -110,17 +136,33 @@ class AccountFragment : Fragment() {
private fun observeLoginStatus() {
accountViewModel.isUserLoggedIn().observe(viewLifecycleOwner) { result ->
result.proceedWhen(
- doOnSuccess = { isLoggedIn ->
+ doOnSuccess = {
getProfileData()
},
doOnError = {
- val isUserLoggedIn = it.payload?.status.toBoolean()
- if (!isUserLoggedIn) {
- (activity as BaseActivity).handleUnAuthorize()
- Toasty.error(requireContext(), "Session expired. Please log in again.", Toast.LENGTH_SHORT, true).show()
+ if (it.exception is NoInternetException) {
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (it.exception is UnAuthorizeException) {
+ (activity as BaseActivity).errorHandler(it.exception)
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (it.exception is ServerErrorException) {
+ (activity as BaseActivity).errorHandler(it.exception)
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_server_error_please_try_again_later),
+ )
} else {
+ Log.d(
+ "login-status",
+ "Error checking login status: ${it.exception?.message}",
+ )
}
- Log.d("login-status", "Error checking login status: ${it.exception?.message}")
},
)
}
@@ -134,10 +176,12 @@ class AccountFragment : Fragment() {
id = data?.userId
email = data?.email
fullName = data?.fullName
+ familyName = data?.familyName
phoneNumber = data?.phoneNumber
binding.layoutProfileUser.etEmail.setText(data?.email)
binding.layoutProfileUser.etFullName.setText(data?.fullName)
binding.layoutProfileUser.etPhoneNumber.setText(data?.phoneNumber)
+ binding.layoutProfileUser.etFamilyName.setText(data?.familyName)
binding.srfProfile.isRefreshing = false
}
},
@@ -146,30 +190,36 @@ class AccountFragment : Fragment() {
if (it.exception is NoInternetException) {
binding.csvProfile.setState(
ContentState.ERROR_NETWORK_GENERAL,
- "Tidak ada internet!",
+ getString(R.string.no_internet_connection),
+ )
+ } else if (it.exception is UnAuthorizeException) {
+ (activity as BaseActivity).errorHandler(it.exception)
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (it.exception is ServerErrorException) {
+ (activity as BaseActivity).errorHandler(it.exception)
+ binding.csvProfile.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_server_error_please_try_again_later),
)
- }
- val errorMessage = it.exception?.message
- if (errorMessage != null && errorMessage.contains("jwt expired")) {
- (activity as BaseActivity).handleUnAuthorize()
} else {
- Log.d("get-profile", "getProfileData: ${it.exception?.message}")
+ Log.d("get-profile", "getProfileData: ${it.exception}")
}
},
)
}
}
- private fun navigateToHome() {
- findNavController().navigate(R.id.menu_home_tab)
- }
-
private fun doEditProfile(
id: String,
fullName: String,
phoneNumber: String,
+ familyName: String,
) {
- val bottomSheetFragment = BottomSheetsEditProfile.newInstance(id, fullName, phoneNumber)
+ val bottomSheetFragment =
+ BottomSheetsEditProfile.newInstance(id, fullName, phoneNumber, familyName)
bottomSheetFragment.show(parentFragmentManager, bottomSheetFragment.tag)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsChangePassword.kt b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsChangePassword.kt
index 5b4010f..21ee87d 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsChangePassword.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsChangePassword.kt
@@ -24,7 +24,8 @@ class BottomSheetsChangePassword : BottomSheetDialogFragment() {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
- binding = FragmentBottomSheetsChangePasswordBinding.inflate(layoutInflater, container, false)
+ binding =
+ FragmentBottomSheetsChangePasswordBinding.inflate(layoutInflater, container, false)
return binding.root
}
@@ -56,7 +57,7 @@ class BottomSheetsChangePassword : BottomSheetDialogFragment() {
newPassword: String,
confirmNewPassword: String,
) {
- sharedViewModelEditProfile.updateProfile(null, null, newPassword, confirmNewPassword)
+ sharedViewModelEditProfile.updateProfile(null, null, null, newPassword, confirmNewPassword)
.observe(viewLifecycleOwner) { result ->
result.proceedWhen(
doOnSuccess = {
@@ -79,7 +80,7 @@ class BottomSheetsChangePassword : BottomSheetDialogFragment() {
).show()
},
doOnLoading = {
- binding.btnSubmit.isVisible = false
+ binding.btnSubmit.isEnabled = false
binding.pbLoading.isVisible = true
},
)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsEditProfile.kt b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsEditProfile.kt
index 2711c9a..9811f91 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsEditProfile.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/BottomSheetsEditProfile.kt
@@ -24,17 +24,20 @@ class BottomSheetsEditProfile : BottomSheetDialogFragment() {
private const val ARG_FULL_NAME = "arg_full_name"
private const val ARG_PHONE_NUMBER = "arg_phone_number"
private const val ARG_USER_ID = "arg_user_id"
+ private const val ARG_FAMILY_NAME = "arg_family_name"
fun newInstance(
id: String?,
fullName: String?,
phoneNumber: String?,
+ familyName: String?,
): BottomSheetsEditProfile {
val fragment = BottomSheetsEditProfile()
val args = Bundle()
args.putString(ARG_FULL_NAME, fullName)
args.putString(ARG_PHONE_NUMBER, phoneNumber)
args.putString(ARG_USER_ID, id)
+ args.putString(ARG_FAMILY_NAME, familyName)
fragment.arguments = args
return fragment
}
@@ -61,8 +64,10 @@ class BottomSheetsEditProfile : BottomSheetDialogFragment() {
id = arguments?.getString(ARG_USER_ID)
val fullName = arguments?.getString(ARG_FULL_NAME)
val phoneNumber = arguments?.getString(ARG_PHONE_NUMBER)
+ val familyName = arguments?.getString(ARG_FAMILY_NAME)
binding.etFullName.setText(fullName)
binding.etPhoneNumber.setText(phoneNumber)
+ binding.etFamilyName.setText(familyName)
}
private fun setClickListeners() {
@@ -82,7 +87,8 @@ class BottomSheetsEditProfile : BottomSheetDialogFragment() {
private fun proceedEditProfile(phoneNumber: String) {
val name = binding.etFullName.text.toString().trim()
- sharedViewModelEditProfile.updateProfile(name, phoneNumber, null, null)
+ val familyName = binding.etFamilyName.text.toString().trim()
+ sharedViewModelEditProfile.updateProfile(name, phoneNumber, familyName, null, null)
.observe(viewLifecycleOwner) { result ->
result.proceedWhen(
doOnSuccess = {
@@ -105,7 +111,7 @@ class BottomSheetsEditProfile : BottomSheetDialogFragment() {
).show()
},
doOnLoading = {
- binding.btnSubmit.isVisible = false
+ binding.btnSubmit.isEnabled = false
binding.pbLoading.isVisible = true
},
)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfile.kt b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfile.kt
index f133491..3f94d0c 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfile.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfile.kt
@@ -14,9 +14,10 @@ class SharedViewModelEditProfile(private val profileRepository: ProfileRepositor
fun updateProfile(
name: String?,
phoneNumber: String?,
+ familyName: String?,
password: String?,
confirmPassword: String?,
) = profileRepository
- .updateProfile(name, phoneNumber, password, confirmPassword)
+ .updateProfile(name, phoneNumber, familyName, password, confirmPassword)
.asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsIssueTicket.kt b/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/IssueTicketBottomSheets.kt
similarity index 93%
rename from app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsIssueTicket.kt
rename to app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/IssueTicketBottomSheets.kt
index 255cc55..c6daaba 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsIssueTicket.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/IssueTicketBottomSheets.kt
@@ -11,7 +11,7 @@ import com.kom.skyfly.R
import com.kom.skyfly.databinding.FragmentBottomSheetsIssueTicketBinding
import com.kom.skyfly.presentation.checkout.flightdetail.FlightDetailActivity
-class BottomSheetsIssueTicket : BottomSheetDialogFragment() {
+class IssueTicketBottomSheets : BottomSheetDialogFragment() {
private lateinit var binding: FragmentBottomSheetsIssueTicketBinding
private var listener: SearchView.OnCloseListener? = null
@@ -21,8 +21,8 @@ class BottomSheetsIssueTicket : BottomSheetDialogFragment() {
adult: Int,
child: Int,
baby: Int,
- ): BottomSheetsIssueTicket {
- val fragment = BottomSheetsIssueTicket()
+ ): IssueTicketBottomSheets {
+ val fragment = IssueTicketBottomSheets()
val args =
Bundle().apply {
putString("transactionId", transactionId)
@@ -80,6 +80,7 @@ class BottomSheetsIssueTicket : BottomSheetDialogFragment() {
putExtra("EXTRAS_ADULT", adult)
putExtra("EXTRAS_CHILD", child)
putExtra("EXTRAS_BABY", baby)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsDialogFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/NotLoginBottomSheets.kt
similarity index 86%
rename from app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsDialogFragment.kt
rename to app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/NotLoginBottomSheets.kt
index f5b67a3..fcc1df6 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/BottomSheetsDialogFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/bottomsheetsdialog/NotLoginBottomSheets.kt
@@ -12,8 +12,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.kom.skyfly.R
import com.kom.skyfly.databinding.FragmentBottomSheetsDialogBinding
import com.kom.skyfly.presentation.login.LoginActivity
+import com.kom.skyfly.presentation.main.MainActivity
-class BottomSheetsDialogFragment : BottomSheetDialogFragment() {
+class NotLoginBottomSheets : BottomSheetDialogFragment() {
private lateinit var binding: FragmentBottomSheetsDialogBinding
private var listener: SearchView.OnCloseListener? = null
@@ -46,11 +47,18 @@ class BottomSheetsDialogFragment : BottomSheetDialogFragment() {
dismiss()
}
binding.ivClose.setOnClickListener {
+ navigateToHome()
listener?.onClose()
dismiss()
}
}
+ private fun navigateToHome() {
+ val intent = Intent(requireContext(), MainActivity::class.java)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ startActivity(intent)
+ }
+
private fun navigateToLogin() {
val intent = Intent(requireContext(), LoginActivity::class.java)
startActivity(intent)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataActivity.kt
index 8977881..c97ca59 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataActivity.kt
@@ -6,10 +6,13 @@ import android.util.Log
import androidx.core.view.isVisible
import com.kom.skyfly.R
import com.kom.skyfly.core.BaseActivity
+import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
import com.kom.skyfly.databinding.ActivityOrdererBiodataBinding
import com.kom.skyfly.presentation.checkout.passengerbiodata.PassengerBiodataActivity
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -23,10 +26,18 @@ class BookersBiodataActivity : BaseActivity() {
private var familyName: String? = null
private var phoneNumber: String? = null
private var id: String? = null
+ private var flightDetailTicket: FlightDetailTicket? = null
+ private var adultCount: Int? = 0
+ private var childCount: Int? = 0
+ private var babyCount: Int? = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
+ flightDetailTicket = intent.getParcelableExtra("EXTRAS_FLIGHT_DETAIL")
+ adultCount = intent.getIntExtra("EXTRA_ADULT_COUNT", 0)
+ childCount = intent.getIntExtra("EXTRA_CHILD_COUNT", 0)
+ babyCount = intent.getIntExtra("EXTRA_BABY_COUNT", 0)
setHaveFamilyName()
setTitleHeader()
getProfileData()
@@ -36,10 +47,14 @@ class BookersBiodataActivity : BaseActivity() {
private fun navigateToPassengerBiodata() {
val intent =
Intent(this, PassengerBiodataActivity::class.java).apply {
+ putExtra("EXTRAS_FLIGHT_DATA", flightDetailTicket)
putExtra("EXTRAS_FULL_NAME", fullName)
putExtra("EXTRAS_FAMILY_NAME", familyName)
putExtra("EXTRAS_EMAIL", email)
putExtra("EXTRAS_PHONE_NUMBER", phoneNumber)
+ putExtra("EXTRA_ADULT_COUNT", adultCount)
+ putExtra("EXTRA_CHILD_COUNT", childCount)
+ putExtra("EXTRA_BABY_COUNT", babyCount)
}
startActivity(intent)
}
@@ -51,6 +66,9 @@ class BookersBiodataActivity : BaseActivity() {
binding.btnSave.setOnClickListener {
navigateToPassengerBiodata()
}
+ binding.layoutHeader.ivBack.setOnClickListener {
+ onBackPressed()
+ }
}
private fun setTitleHeader() {
@@ -65,11 +83,15 @@ class BookersBiodataActivity : BaseActivity() {
tilFamilyName.isVisible = true
etFamilyName.isVisible = true
tvFamilyName.isVisible = true
+ val trackDrawable = scHaveFamilyName.trackDrawable
+ trackDrawable?.setTint(resources.getColor(R.color.md_theme_primaryFixed_mediumContrast))
} else {
tvFamilyName.isVisible = false
tilFamilyName.isVisible = false
etFamilyName.isVisible = false
tvFamilyName.isVisible = false
+ val trackDrawable = scHaveFamilyName.trackDrawable
+ trackDrawable?.setTint(resources.getColor(R.color.grey))
}
}
}
@@ -79,12 +101,15 @@ class BookersBiodataActivity : BaseActivity() {
bookersBiodataViewModel.getProfile().observe(this) { result ->
result.proceedWhen(
doOnSuccess = {
+ binding.btnSave.isEnabled = true
it.payload.let { data ->
id = data?.userId
email = data?.email
familyName = data?.familyName
fullName = data?.fullName
phoneNumber = data?.phoneNumber
+ binding.layoutHeader.tvTitleHeader.text =
+ getString(R.string.text_bookers_biodata)
binding.layoutFormCustomerBiodata.etFullName.setText(data?.fullName)
binding.layoutFormCustomerBiodata.etEmail.setText(data?.email)
binding.layoutFormCustomerBiodata.etNoTlp.setText(data?.phoneNumber)
@@ -93,20 +118,32 @@ class BookersBiodataActivity : BaseActivity() {
}
},
doOnError = {
+ binding.btnSave.isEnabled = false
binding.main.isRefreshing = false
if (it.exception is NoInternetException) {
binding.csvBookers.setState(
ContentState.ERROR_NETWORK_GENERAL,
- "No internet connection!!",
+ getString(R.string.no_internet_connection),
+ )
+ } else if (it.exception is UnAuthorizeException) {
+ errorHandler(it.exception)
+ binding.csvBookers.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (it.exception is ServerErrorException) {
+ errorHandler(it.exception)
+ binding.csvBookers.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_server_error_please_try_again_later),
)
- }
- val errorMessage = it.exception?.message
- if (errorMessage != null && errorMessage.contains("jwt expired")) {
- handleUnAuthorize()
} else {
Log.d("get-profile", "getProfileData: ${it.exception?.message}")
}
},
+ doOnLoading = {
+ binding.btnSave.isEnabled = false
+ },
)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketActivity.kt
index 8275afd..526642c 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketActivity.kt
@@ -4,17 +4,21 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
import com.kom.skyfly.databinding.ActivityCheckoutTicketBinding
import com.kom.skyfly.presentation.checkout.payment.PaymentActivity
+import com.kom.skyfly.presentation.common.views.ContentState
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.formatToRupiah
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.viewModel
-class CheckoutTicketActivity : AppCompatActivity() {
+class CheckoutTicketActivity : BaseActivity() {
private val binding: ActivityCheckoutTicketBinding by lazy {
ActivityCheckoutTicketBinding.inflate(layoutInflater)
}
@@ -45,16 +49,51 @@ class CheckoutTicketActivity : AppCompatActivity() {
checkoutTicketViewModel.getTransactionById(id).observe(this) { result ->
result.proceedWhen(
doOnSuccess = {
+ binding.btnProceedToPayment.isEnabled = true
+ binding.main.isRefreshing = false
+ binding.shmProgressFlightTicket.isVisible = false
+ binding.layoutFlightDetails.cvFlightDetails.isVisible = true
+ binding.layoutFlightDetails.cvPriceDetails.isVisible = true
+ binding.layoutFlightDetails.tvTrip.isVisible = true
val response = result.payload
setTransactionDetailData(response)
- Log.d("GetTransactionById", "getTransactionDataById: $response")
},
doOnError = {
+ binding.btnProceedToPayment.isEnabled = false
+ binding.shmProgressFlightTicket.isVisible = false
+ binding.main.isRefreshing = false
+ if (it.exception is NoInternetException) {
+ binding.csvCheckoutTicket.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ "No internet connection!!",
+ )
+ } else if (it.exception is UnAuthorizeException) {
+ errorHandler(it.exception)
+ binding.csvCheckoutTicket.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (it.exception is ServerErrorException) {
+ errorHandler(it.exception)
+ binding.csvCheckoutTicket.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_something_went_wrong),
+ R.drawable.img_empty_data,
+ )
+ }
Log.d(
"GetTransactionByIdError",
"getTransactionDataById: ${it.exception?.message}",
)
},
+ doOnLoading = {
+ binding.layoutFlightDetails.cvFlightDetails.isVisible = false
+ binding.layoutFlightDetails.cvPriceDetails.isVisible = false
+ binding.layoutFlightDetails.tvTrip.isVisible = false
+ binding.main.isRefreshing = true
+ binding.btnProceedToPayment.isEnabled = false
+ binding.shmProgressFlightTicket.isVisible = true
+ },
)
}
}
@@ -166,6 +205,12 @@ class CheckoutTicketActivity : AppCompatActivity() {
intent.putExtra("EXTRAS_BABY", baby)
startActivity(intent)
}
+ binding.layoutHeader.ivBack.setOnClickListener {
+ onBackPressed()
+ }
+ binding.main.setOnRefreshListener {
+ getTransactionDataById(transactionId)
+ }
}
private fun setTitleHeader() {
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatActivity.kt
index 1bddaa4..f3b4e14 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatActivity.kt
@@ -1,28 +1,32 @@
package com.kom.skyfly.presentation.checkout.chooseseat
+import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
+import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
import com.kom.skyfly.data.model.passenger.PassengerData
import com.kom.skyfly.data.source.network.model.transaction.request.Bookers
import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
import com.kom.skyfly.databinding.ActivityChooseSeatBinding
-import com.kom.skyfly.presentation.checkout.bookersbiodata.BookersBiodataActivity
import com.kom.skyfly.presentation.checkout.checkoutticket.CheckoutTicketActivity
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import dev.jahidhasanco.seatbookview.SeatBookView
import dev.jahidhasanco.seatbookview.SeatClickListener
import es.dmoral.toasty.Toasty
import org.koin.androidx.viewmodel.ext.android.viewModel
+import java.util.Locale
-class ChooseSeatActivity : AppCompatActivity() {
+class ChooseSeatActivity : BaseActivity() {
private val binding: ActivityChooseSeatBinding by lazy {
ActivityChooseSeatBinding.inflate(layoutInflater)
}
@@ -51,27 +55,29 @@ class ChooseSeatActivity : AppCompatActivity() {
private var baby: Int = 0
private var paymentUrl: String? = null
private var transactionId: String? = null
+ private var flightDetailTicket: FlightDetailTicket? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
-
+ flightDetailTicket = intent.getParcelableExtra("EXTRAS_FLIGHT_DETAIL")
fullNames = intent.getStringExtra("EXTRAS_FULL_NAME")
familyName = intent.getStringExtra("EXTRAS_FAMILY_NAME")
email = intent.getStringExtra("EXTRAS_EMAIL")
phoneNumber = intent.getStringExtra("EXTRAS_PHONE_NUMBER")
passengerDataList = intent.getParcelableArrayListExtra("EXTRAS_PASSENGERS")!!
+
adult = intent.getIntExtra("EXTRAS_ADULT", 0)
children = intent.getIntExtra("EXTRAS_CHILD", 0)
baby = intent.getIntExtra("EXTRAS_BABY", 0)
- passengerDataList.forEachIndexed { index, passenger ->
- Log.d("PassengerData", "Passenger $index: $passenger")
- }
+
setTitleHeader()
- flightId = "clxrh0w6m000zokfpvb5sobt8"
- getAllFlightSeatData(flightId!!)
- getFlightSeatData(flightId!!)
- getFlightPrice(flightId!!)
+ flightId = flightDetailTicket?.id
+ flightId?.let {
+ getAllFlightSeatData(it)
+ getFlightSeatData(it)
+ getFlightPrice(it)
+ }
setOnClickListener()
}
@@ -79,12 +85,25 @@ class ChooseSeatActivity : AppCompatActivity() {
binding.btnSave.setOnClickListener {
flightId?.let { createTransaction(it, passengerDataList) }
}
+ binding.layoutHeader.ivBack.setOnClickListener {
+ onBackPressed()
+ }
+ binding.main.setOnRefreshListener {
+ flightId?.let {
+ getAllFlightSeatData(it)
+ getFlightSeatData(it)
+ getFlightPrice(it)
+ }
+ }
}
private fun getFlightSeatData(flightId: String) {
chooseSeatViewModel.getFlightSeat(flightId).observe(this) { result ->
result.proceedWhen(
doOnSuccess = { resultWrapper ->
+ binding.btnSave.isEnabled = true
+ binding.pbLoading.isVisible = false
+ binding.main.isRefreshing = false
val payload = resultWrapper.payload
chooseSeatViewModel.setSeatList(payload?.flightSeat.orEmpty())
if (payload != null) {
@@ -99,46 +118,79 @@ class ChooseSeatActivity : AppCompatActivity() {
binding.csvSeatView.setState(ContentState.SUCCESS)
},
doOnEmpty = {
+ binding.btnSave.isEnabled = true
+ binding.pbLoading.isVisible = false
binding.shmProgressSeatView.isVisible = false
binding.csvSeatView.setState(ContentState.EMPTY, "Empty seat!")
},
doOnError = { error ->
+ binding.btnSave.isEnabled = false
+ binding.main.isRefreshing = false
binding.shmProgressSeatView.isVisible = false
if (error.exception is NoInternetException) {
- binding.csvSeatView.setState(ContentState.ERROR_NETWORK)
+ binding.csvSeatView.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvSeatView.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvSeatView.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
} else {
binding.csvSeatView.setState(ContentState.ERROR_GENERAL)
}
Log.e("ChooseSeatActivity", "Error: ${error.exception?.message}")
},
doOnLoading = {
+ binding.btnSave.isEnabled = false
+ binding.main.isRefreshing = true
binding.shmProgressSeatView.isVisible = true
},
)
}
}
+ @SuppressLint("StringFormatMatches")
private fun getAllFlightSeatData(flightId: String) {
chooseSeatViewModel.getAllFlightSeat(flightId).observe(this) { result ->
result.proceedWhen(
doOnSuccess = { resultWrapper ->
- Log.d("SeatDatas", "getAllFlightSeatData: $resultWrapper")
+ binding.main.isRefreshing = false
+ binding.btnSave.isEnabled = true
+ binding.pbLoading.isVisible = false
seatTotal = resultWrapper.payload?.size ?: 0
resultWrapper.payload.let {
it?.map { response ->
seatType = response.type.orEmpty()
- binding.tvSeatTitle.text = "$seatType - $seatTotal Seat"
+ binding.tvSeatTitle.text =
+ getString(R.string.text_seat_data_title, seatType, seatTotal)
}
}
},
doOnLoading = {
- binding.tvSeatTitle.text = "Loading..."
+ binding.btnSave.isEnabled = false
+ binding.main.isRefreshing = false
+ binding.tvSeatTitle.text = getString(R.string.text_loading)
},
doOnEmpty = {
- binding.tvSeatTitle.text = "SeatView Kosong"
+ binding.btnSave.isEnabled = true
+ binding.pbLoading.isVisible = false
+ binding.main.isRefreshing = false
+ binding.tvSeatTitle.text = getString(R.string.text_empty_seat)
},
doOnError = {
- binding.tvSeatTitle.text = "Error"
+ binding.btnSave.isEnabled = false
+ binding.main.isRefreshing = true
+ binding.tvSeatTitle.text = getString(R.string.text_empty_seat)
},
)
}
@@ -148,6 +200,9 @@ class ChooseSeatActivity : AppCompatActivity() {
chooseSeatViewModel.getFlightPrice(flightId).observe(this) { result ->
result.proceedWhen(
doOnSuccess = {
+ binding.main.isRefreshing = false
+ binding.btnSave.isEnabled = true
+ binding.pbLoading.isVisible = false
val payload = it.payload
payload?.let { response ->
seatId = response.first
@@ -177,21 +232,12 @@ class ChooseSeatActivity : AppCompatActivity() {
val actualSelectedIdx = selectedIdList.map { it - 1 }
chooseSeatViewModel.setSelectedSeatList(actualSelectedIdx)
- if (currentPassengerIndex >= passengerDataList.size) {
- Toasty.info(
- this@ChooseSeatActivity,
- "All passengers have been assigned seats.",
- Toast.LENGTH_SHORT,
- ).show()
- return
- }
-
val selectedSeatIndex =
selectedIdList.firstOrNull()
if (selectedSeatIndex == null) {
Toasty.error(
this@ChooseSeatActivity,
- "No seat selected.",
+ getString(R.string.text_no_seat_selected),
Toast.LENGTH_SHORT,
).show()
return
@@ -201,29 +247,17 @@ class ChooseSeatActivity : AppCompatActivity() {
if (correctedSeatIndex < 0 || correctedSeatIndex >= seatId.size) {
Toasty.error(
this@ChooseSeatActivity,
- "Invalid seat selected.",
+ getString(R.string.text_invalid_seat_selected),
Toast.LENGTH_SHORT,
).show()
return
}
- val selectedSeatId = seatId[correctedSeatIndex]
- val currentPassenger = passengerDataList[currentPassengerIndex]
-
- Toasty.success(
- this@ChooseSeatActivity,
- "Assigned seat $selectedSeatId to ${currentPassenger.fullName}",
- Toast.LENGTH_SHORT,
- ).show()
-
currentPassengerIndex++
if (currentPassengerIndex < passengerDataList.size) {
- Toasty.warning(
- this@ChooseSeatActivity,
- "Select seat for ${passengerDataList[currentPassengerIndex].fullName}",
- Toast.LENGTH_SHORT,
- ).show()
+ binding.btnSave.isEnabled = false
} else {
+ binding.btnSave.isEnabled = true
Toasty.info(
this@ChooseSeatActivity,
"All passengers have been assigned seats.",
@@ -235,7 +269,7 @@ class ChooseSeatActivity : AppCompatActivity() {
override fun onBookedSeatClick(view: View) {
Toasty.info(
this@ChooseSeatActivity,
- "Seat is Booked!",
+ getString(R.string.text_seat_is_booked),
Toast.LENGTH_SHORT,
).show()
}
@@ -243,7 +277,7 @@ class ChooseSeatActivity : AppCompatActivity() {
override fun onReservedSeatClick(view: View) {
Toasty.info(
this@ChooseSeatActivity,
- "Seat is Reserved!",
+ getString(R.string.text_seat_is_reserved),
Toast.LENGTH_SHORT,
).show()
}
@@ -273,10 +307,7 @@ class ChooseSeatActivity : AppCompatActivity() {
.observe(this) { result ->
result.proceedWhen(
doOnSuccess = {
- Toasty.success(this, "Success Create Transaction!!!!!", Toast.LENGTH_SHORT)
- .show()
- val intent = Intent(this, BookersBiodataActivity::class.java)
- startActivity(intent)
+ binding.main.isRefreshing = false
it.payload?.let { response ->
paymentUrl = response.redirectUrl
transactionId = response.transactionId
@@ -284,18 +315,28 @@ class ChooseSeatActivity : AppCompatActivity() {
navigateToPayment()
},
doOnError = {
+ binding.main.isRefreshing = false
Log.e("ChooseSeatActivity", "Error: ${it.exception?.message}")
},
doOnLoading = {
+ binding.main.isRefreshing = true
binding.pbLoading.isVisible = true
- binding.btnSave.isVisible = false
+ binding.btnSave.isEnabled = false
},
)
}
}
private fun setTitleHeader() {
- binding.layoutHeader.tvTitleHeader.text = getString(R.string.text_header_choose_seat)
+ binding.layoutHeader.tvTitleHeader.text =
+ getString(
+ R.string.choose_seat,
+ flightDetailTicket?.departureCountryCode,
+ flightDetailTicket?.arrivalCountryCode,
+ flightDetailTicket?.seatClass?.lowercase(
+ Locale.ROOT,
+ ),
+ )
}
private fun navigateToPayment() {
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatViewModel.kt
index ea6c87e..24ba461 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/chooseseat/ChooseSeatViewModel.kt
@@ -46,7 +46,7 @@ class ChooseSeatViewModel(
fullName = passenger.fullName,
dob = "${passenger.dob} 10:00:00",
validityPeriod = "${passenger.validityPeriod} 10:00:00",
- familyName = passenger.familyName,
+ familyName = passenger.familyName.orEmpty(),
citizenship = passenger.citizenship,
passport = passenger.passport,
issuingCountry = "Indonesia",
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailActivity.kt
index 7c1cc9c..1aa8d12 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailActivity.kt
@@ -1,19 +1,27 @@
package com.kom.skyfly.presentation.checkout.flightdetail
+import android.annotation.SuppressLint
+import android.content.Intent
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
+import coil.load
import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
import com.kom.skyfly.databinding.ActivityFlightDetailBinding
+import com.kom.skyfly.presentation.common.views.ContentState
+import com.kom.skyfly.presentation.main.MainActivity
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.formatToRupiah
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.viewModel
-class FlightDetailActivity : AppCompatActivity() {
+class FlightDetailActivity : BaseActivity() {
private val binding: ActivityFlightDetailBinding by lazy {
ActivityFlightDetailBinding.inflate(layoutInflater)
}
@@ -27,14 +35,35 @@ class FlightDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- setSwipeRefresh()
transactionId = intent.getStringExtra("EXTRAS_TRANSACTION_ID")
adult = intent.getIntExtra("EXTRAS_ADULT", 0)
children = intent.getIntExtra("EXTRAS_CHILD", 0)
baby = intent.getIntExtra("EXTRAS_BABY", 0)
+ setTitleHeader()
+ setSwipeRefresh()
+ setOnClickListeners()
getTicketDetail(transactionId)
}
+ private fun setOnClickListeners() {
+ binding.layoutHeader.ivBack.setOnClickListener {
+ navigateToHome()
+ }
+ binding.btnToHome.setOnClickListener {
+ navigateToHome()
+ }
+ }
+
+ private fun navigateToHome() {
+ val intent = Intent(this, MainActivity::class.java)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ startActivity(intent)
+ }
+
+ private fun setTitleHeader() {
+ binding.layoutHeader.tvTitleHeader.text = getString(R.string.text_ticket_detail)
+ }
+
private fun setSwipeRefresh() {
binding.main.setOnRefreshListener {
getTicketDetail(transactionId)
@@ -46,40 +75,76 @@ class FlightDetailActivity : AppCompatActivity() {
viewModel.getTransactionById(id).observe(this) { result ->
result.proceedWhen(
doOnSuccess = {
+ binding.btnToHome.isEnabled = true
+ binding.layoutFlightDetails.cvFlightDetails.isVisible = true
+ binding.layoutFlightDetails.cvPriceDetails.isVisible = true
+ binding.layoutFlightDetails.tvPaymentStatus.isVisible = true
binding.main.isRefreshing = false
val response = result.payload
+ binding.shmProgressFlightTicket.isVisible = false
setTransactionDetailData(response)
Log.d("GetTransactionById", "getTransactionDataById: $response")
},
doOnError = {
+ binding.btnToHome.isEnabled = true
binding.main.isRefreshing = false
+ binding.shmProgressFlightTicket.isVisible = false
+ when (it.exception) {
+ is NoInternetException -> {
+ binding.csvFlightDetail.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.no_internet_connection),
+ )
+ }
+
+ is UnAuthorizeException -> {
+ errorHandler(it.exception)
+ binding.csvFlightDetail.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ }
+
+ is ServerErrorException -> {
+ errorHandler(it.exception)
+ binding.csvFlightDetail.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_something_went_wrong),
+ R.drawable.img_empty_data,
+ )
+ }
+ }
Log.d(
"GetTransactionByIdError",
"getTransactionDataById: ${it.exception?.message}",
)
},
doOnLoading = {
+ binding.csvFlightDetail.isVisible = false
+ binding.btnToHome.isEnabled = false
+ binding.layoutFlightDetails.cvFlightDetails.isVisible = false
+ binding.layoutFlightDetails.cvPriceDetails.isVisible = false
+ binding.layoutFlightDetails.tvPaymentStatus.isVisible = false
binding.main.isRefreshing = true
+ binding.shmProgressFlightTicket.isVisible = true
},
)
}
}
}
+ @SuppressLint("StringFormatMatches")
private fun setTransactionDetailData(response: TransactionDetailResponses?) {
var departureDate: String? = null
var departureTime: String? = null
var departureAirport: String? = null
var departureTerminal: String? = null
- var departureCity: String? = null
var airlines: String? = null
var seatClass: String? = null
var flightNumber: String? = null
var arrivalDate: String? = null
var arrivalTime: String? = null
var destinationAirport: String? = null
- var destinationCity: String? = null
- var flightDuration: String? = null
val tax: Int? = response?.data?.tax
val totalPrice: Int? = response?.data?.totalPrice
var totalPricePassengerAdult: Int? = 0
@@ -89,36 +154,48 @@ class FlightDetailActivity : AppCompatActivity() {
val paymentStatus: String? = response?.data?.status
val bookingCode: String? = response?.data?.booking?.code
val transactionDetails = response?.data?.transactionDetails
+ var airlinesImg: String? = null
response?.data?.transactionDetails?.map {
departureDate = it?.flight?.departure?.date
departureTime = it?.flight?.departure?.time
- departureCity = it?.flight?.departureAirport?.city
arrivalDate = it?.flight?.arrival?.date
arrivalTime = it?.flight?.arrival?.time
departureAirport = it?.flight?.departureAirport?.name
departureTerminal = it?.flight?.airline?.terminal
- destinationCity = it?.flight?.destinationAirport?.city
flightNumber = it?.flight?.airline?.code
destinationAirport = it?.flight?.destinationAirport?.name
- flightDuration = it?.flight?.flightDuration
airlines = it?.flight?.airline?.name
seatClass = it?.seat?.type
passengerCategory = it?.passengerCategory
- if (passengerCategory == "ADULT") {
- totalPricePassengerAdult = it?.totalPrice?.times(adult)
- } else if (passengerCategory == "CHILD") {
- totalPricePassengerChild = it?.totalPrice?.times(children)
- } else if (passengerCategory == "INFRANT") {
- totalPricePassengerBaby = it?.totalPrice?.times(baby)
+ airlinesImg = it?.flight?.airline?.image
+ when (passengerCategory) {
+ "ADULT" -> {
+ totalPricePassengerAdult = it?.totalPrice?.times(adult)
+ }
+
+ "CHILD" -> {
+ totalPricePassengerChild = it?.totalPrice?.times(children)
+ }
+
+ "INFRANT" -> {
+ totalPricePassengerBaby = it?.totalPrice?.times(baby)
+ }
}
}
+ binding.layoutFlightDetails.tvTitlePassenger.text = getString(R.string.text_empty)
+ binding.layoutFlightDetails.tvCitizenship.text = getString(R.string.text_empty)
with(binding.layoutFlightDetails) {
+ ivAirline.load(
+ airlinesImg,
+ ) {
+ error(R.drawable.img_airline)
+ }
tvDetailDepartureDate.text = departureDate
tvDetailDepartureTime.text = departureTime
- tvDetailDepartureAirport.text = "$departureAirport -"
- tvDetailTerminal.text = departureTerminal
- tvDetailAirline.text = "$airlines - "
+ tvDetailDepartureAirport.text =
+ getString(R.string.text_departure_terminal, departureAirport, departureTerminal)
+ tvDetailAirline.text = getString(R.string.text_detail_airlines, airlines)
tvDetailClass.text = seatClass
tvDetailFlightNumber.text = flightNumber
tvDetailArrivalDate.text = arrivalDate
@@ -130,11 +207,11 @@ class FlightDetailActivity : AppCompatActivity() {
transactionDetails?.forEachIndexed { index, detail ->
val passengerName = detail?.name ?: "Unknown"
- val citizenship = detail?.citizenship ?: "Unknown"
+ val userPassportId = detail?.passport ?: "Unknown"
tvTitlePassenger.append(
"Passenger ${index + 1} : $passengerName\n" +
- "Citizenship : $citizenship\n",
+ "ID : $userPassportId\n",
)
}
@@ -168,7 +245,7 @@ class FlightDetailActivity : AppCompatActivity() {
}
if (adult > 0) {
- tvTotalByAgeGroupAdult.text = "$adult Adult"
+ tvTotalByAgeGroupAdult.text = getString(R.string.text_adult_total, adult)
tvTotalPriceByAgeGroupAdult.text =
totalPricePassengerAdult.formatToRupiah().toString()
tvTotalByAgeGroupAdult.isVisible = true
@@ -179,7 +256,7 @@ class FlightDetailActivity : AppCompatActivity() {
}
if (children > 0) {
- tvTotalByAgeGroupChild.text = "$children Child"
+ tvTotalByAgeGroupChild.text = getString(R.string.text_child_total, children)
tvTotalPriceByAgeGroupChild.text =
totalPricePassengerChild.formatToRupiah().toString()
tvTotalByAgeGroupChild.isVisible = true
@@ -190,7 +267,7 @@ class FlightDetailActivity : AppCompatActivity() {
}
if (baby > 0) {
- tvTotalByAgeGroupBaby.text = "$baby Baby"
+ tvTotalByAgeGroupBaby.text = getString(R.string.text_baby_total, baby)
tvTotalPriceByAgeGroupBaby.text =
totalPricePassengerBaby.formatToRupiah().toString()
tvTotalByAgeGroupBaby.isVisible = true
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataActivity.kt
index 25a61bd..443b8c4 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataActivity.kt
@@ -8,13 +8,13 @@ import android.widget.EditText
import androidx.recyclerview.widget.LinearLayoutManager
import com.kom.skyfly.R
import com.kom.skyfly.core.BaseActivity
+import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
import com.kom.skyfly.data.model.passenger.PassengerData
import com.kom.skyfly.databinding.ActivityPassengerBiodataBinding
import com.kom.skyfly.presentation.checkout.chooseseat.ChooseSeatActivity
import com.kom.skyfly.presentation.checkout.passengerbiodata.adapter.PassengerItem
import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.GroupieViewHolder
-import org.koin.androidx.viewmodel.ext.android.viewModel
import java.util.Calendar
class PassengerBiodataActivity : BaseActivity() {
@@ -22,7 +22,6 @@ class PassengerBiodataActivity : BaseActivity() {
private var passengerDataList: List = emptyList()
private val groupAdapter = GroupAdapter()
- private val passengerBiodataViewModel: PassengerBiodataViewModel by viewModel()
private var email: String? = null
private var fullNames: String? = null
@@ -31,18 +30,20 @@ class PassengerBiodataActivity : BaseActivity() {
private var adult = 0
private var children = 0
private var baby = 0
+ private var flightDetailTicket: FlightDetailTicket? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPassengerBiodataBinding.inflate(layoutInflater)
setContentView(binding.root)
+ flightDetailTicket = intent.getParcelableExtra("EXTRAS_FLIGHT_DATA")
fullNames = intent.getStringExtra("EXTRAS_FULL_NAME")
familyName = intent.getStringExtra("EXTRAS_FAMILY_NAME")
email = intent.getStringExtra("EXTRAS_EMAIL")
phoneNumber = intent.getStringExtra("EXTRAS_PHONE_NUMBER")
- adult = intent.getIntExtra("EXTRAS_ADULT", 0)
- children = intent.getIntExtra("EXTRAS_CHILD", 0)
- baby = intent.getIntExtra("EXTRAS_BABY", 0)
+ adult = intent.getIntExtra("EXTRA_ADULT_COUNT", 0)
+ children = intent.getIntExtra("EXTRA_CHILD_COUNT", 0)
+ baby = intent.getIntExtra("EXTRA_BABY_COUNT", 0)
setUpRecyclerView()
setTitleHeader()
handleNextButtonClick()
@@ -56,10 +57,6 @@ class PassengerBiodataActivity : BaseActivity() {
}
private fun setUpRecyclerView() {
- adult = 1
- children = 0
- baby = 0
-
binding.rvPassengerForm.layoutManager = LinearLayoutManager(this)
binding.rvPassengerForm.adapter = groupAdapter
@@ -76,6 +73,7 @@ class PassengerBiodataActivity : BaseActivity() {
i < adult + children -> "CHILD"
else -> "INFRANT"
}
+
groupAdapter.add(
PassengerItem(
passengerType,
@@ -125,6 +123,7 @@ class PassengerBiodataActivity : BaseActivity() {
}
val intent = Intent(this, ChooseSeatActivity::class.java)
+ intent.putExtra("EXTRAS_FLIGHT_DETAIL", flightDetailTicket)
intent.putParcelableArrayListExtra("EXTRAS_PASSENGERS", ArrayList(passengerDataList))
intent.putExtra("EXTRAS_FULL_NAME", fullNames)
intent.putExtra("EXTRAS_FAMILY_NAME", familyName)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataViewModel.kt
deleted file mode 100644
index 876d3c4..0000000
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/PassengerBiodataViewModel.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.kom.skyfly.presentation.checkout.passengerbiodata
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.asLiveData
-import com.kom.skyfly.data.repository.transaction.TransactionRepository
-import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
-import kotlinx.coroutines.Dispatchers
-
-/**
-Written by Komang Yuda Saputra
-Github : https://github.com/YudaSaputraa
- **/
-class PassengerBiodataViewModel(private val transactionRepository: TransactionRepository) :
- ViewModel() {
- fun createTransaction(
- flightId: String,
- adult: Int,
- child: Int,
- baby: Int,
- transactionRequest: TransactionRequest,
- ) = transactionRepository.createTransaction(flightId, adult, child, baby, transactionRequest)
- .asLiveData(Dispatchers.IO)
-}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerAdapter.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerAdapter.kt
deleted file mode 100644
index ca8819c..0000000
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerAdapter.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-// package com.kom.skyfly.presentation.checkout.passengerbiodata.adapter
-//
-// import android.app.DatePickerDialog
-// import android.view.LayoutInflater
-// import android.view.ViewGroup
-// import android.widget.ArrayAdapter
-// import android.widget.EditText
-// import android.widget.Spinner
-// import androidx.core.view.isVisible
-// import androidx.recyclerview.widget.RecyclerView
-// import com.kom.skyfly.R
-// import com.kom.skyfly.data.model.passenger.PassengerData
-// import com.kom.skyfly.databinding.ItemFormPassengerBiodataBinding
-// import java.util.Calendar
-//
-//
-// class PassengerAdapter(
-// private val recyclerView: RecyclerView,
-// private val adultsCount: Int,
-// private val childrenCount: Int,
-// private val infantCount: Int,
-// private val itemClick: (Int) -> Unit,
-// ) : RecyclerView.Adapter() {
-// private val passengerDataList = mutableListOf()
-//
-// override fun onCreateViewHolder(
-// parent: ViewGroup,
-// viewType: Int,
-// ): PassengerViewHolder {
-// val binding =
-// ItemFormPassengerBiodataBinding.inflate(
-// LayoutInflater.from(parent.context),
-// parent,
-// false,
-// )
-// return PassengerViewHolder(binding)
-// }
-//
-// override fun onBindViewHolder(
-// holder: PassengerViewHolder,
-// position: Int,
-// ) {
-// holder.bindView(position)
-// }
-//
-// override fun getItemCount(): Int {
-// return adultsCount + childrenCount + infantCount
-// }
-//
-// inner class PassengerViewHolder(
-// private val binding: ItemFormPassengerBiodataBinding,
-// ) : RecyclerView.ViewHolder(binding.root) {
-// init {
-// ArrayAdapter.createFromResource(
-// itemView.context,
-// R.array.titles_array,
-// android.R.layout.simple_spinner_item,
-// ).also { adapter ->
-// adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
-// binding.spinnerTitle.adapter = adapter
-// }
-//
-// binding.scHaveFamilyName.setOnCheckedChangeListener { _, isChecked ->
-// binding.tvFamilyName.isVisible = isChecked
-// binding.tilFamilyName.isVisible = isChecked
-// }
-// binding.etDate.setOnClickListener {
-// val position = adapterPosition
-// showDatePickerDialog(binding.etDate)
-// }
-// binding.etValidUntil.setOnClickListener {
-// val position = adapterPosition
-// showDatePickerDialog(binding.etValidUntil)
-// }
-//
-// binding.ivCalendar.setOnClickListener {
-// val position = adapterPosition
-// showDatePickerDialog(binding.etDate)
-// }
-// binding.ivCalendarValidUntil.setOnClickListener {
-// val position = adapterPosition
-// showDatePickerDialog(binding.etValidUntil)
-// }
-// }
-//
-// private fun showDatePickerDialog(editText: EditText) {
-// val c = Calendar.getInstance()
-// val year = c.get(Calendar.YEAR)
-// val month = c.get(Calendar.MONTH)
-// val day = c.get(Calendar.DAY_OF_MONTH)
-// val datePickerDialog =
-// DatePickerDialog(
-// itemView.context,
-// { _, year, monthOfYear, dayOfMonth ->
-// val date = "$dayOfMonth/${monthOfYear + 1}/$year"
-// editText.setText(date)
-// },
-// year,
-// month,
-// day,
-// )
-// datePickerDialog.show()
-// }
-//
-// fun bindView(passengerNumber: Int) {
-// itemView.apply {
-// val passengerIndex = passengerNumber + 1
-// when {
-// passengerNumber < adultsCount -> {
-// binding.tvPassengerType.text = "Passenger $passengerIndex - Adult"
-// }
-//
-// passengerNumber < adultsCount + childrenCount -> {
-// binding.tvPassengerType.text = "Passenger $passengerIndex - Children"
-// }
-//
-// else -> {
-// binding.tvPassengerType.text = "Passenger $passengerIndex - Baby"
-// }
-// }
-// binding.etFullName.setOnFocusChangeListener { _, hasFocus ->
-// if (!hasFocus) {
-// savePassengerData(adapterPosition)
-// }
-// }
-// binding.etDate.setOnFocusChangeListener { _, hasFocus ->
-// if (!hasFocus) {
-// savePassengerData(adapterPosition)
-// }
-// }
-// }
-// }
-// }
-//
-// fun saveAllPassengerData() {
-// for (i in 0 until itemCount) {
-// savePassengerData(i)
-// }
-// }
-//
-// private fun savePassengerData(position: Int) {
-// val itemView = recyclerView.findViewHolderForAdapterPosition(position)?.itemView
-// val spinnerTitle = itemView?.findViewById(R.id.spinner_title)
-// val etFullName = itemView?.findViewById(R.id.et_full_name)
-// val etFamilyName = itemView?.findViewById(R.id.et_family_name)
-// val etDateOfBirth = itemView?.findViewById(R.id.et_date)
-// val nationality = itemView?.findViewById(R.id.et_country)
-// val idOrPassport = itemView?.findViewById(R.id.et_passport)
-// val etValidUntil = itemView?.findViewById(R.id.et_valid_until)
-// if (spinnerTitle != null && etFullName != null && etDateOfBirth != null && nationality != null && idOrPassport != null && etValidUntil != null) {
-// val passengerData =
-// PassengerData(
-// spinnerTitle.selectedItem.toString(),
-// etFullName.text.toString(),
-// etFamilyName?.text.toString(),
-// etDateOfBirth.text.toString(),
-// nationality.text.toString(),
-// idOrPassport.text.toString(),
-// etValidUntil.text.toString(),
-// )
-// if (position < passengerDataList.size) {
-// passengerDataList[position] = passengerData
-// } else {
-// passengerDataList.add(passengerData)
-// }
-// }
-// }
-//
-// fun getPassengerDataList(): List {
-// saveAllPassengerData()
-// return passengerDataList
-// }
-// }
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerItem.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerItem.kt
index 72c35b5..44a39c5 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerItem.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/passengerbiodata/adapter/PassengerItem.kt
@@ -1,8 +1,10 @@
package com.kom.skyfly.presentation.checkout.passengerbiodata.adapter
+import android.util.Log
import android.view.View
import android.widget.ArrayAdapter
import android.widget.EditText
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.kom.skyfly.R
import com.kom.skyfly.data.model.passenger.PassengerData
@@ -43,7 +45,32 @@ class PassengerItem(
scHaveFamilyName.setOnCheckedChangeListener { _, isChecked ->
tvFamilyName.isVisible = isChecked
tilFamilyName.isVisible = isChecked
+ if (isChecked) {
+ val trackDrawable = scHaveFamilyName.trackDrawable
+ trackDrawable?.setTint(
+ ContextCompat.getColor(
+ viewBinding.root.context,
+ R.color.md_theme_primaryFixed_mediumContrast,
+ ),
+ )
+ } else {
+ val trackDrawable = scHaveFamilyName.trackDrawable
+ trackDrawable?.setTint(
+ ContextCompat.getColor(
+ viewBinding.root.context,
+ R.color.grey,
+ ),
+ )
+ }
}
+ if (passengerTypeLabel == "CHILD" || passengerTypeLabel == "INFRANT") {
+ tilPassport.isVisible = false
+ tvPassport.isVisible = false
+ tvValidUntil.isVisible = false
+ ivCalendarValidUntil.isVisible = false
+ tilValidUntil.isVisible = false
+ }
+ Log.d("passengerType", "bind: $passengerTypeLabel")
etDate.setOnClickListener { onDateClick(etDate) }
etValidUntil.setOnClickListener { onValidUntilClick(etValidUntil) }
diff --git a/app/src/main/java/com/kom/skyfly/presentation/checkout/payment/PaymentActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/checkout/payment/PaymentActivity.kt
index 01477b6..7808318 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/checkout/payment/PaymentActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/checkout/payment/PaymentActivity.kt
@@ -1,6 +1,6 @@
package com.kom.skyfly.presentation.checkout.payment
-import BottomSheetsIssueTicket
+import IssueTicketBottomSheets
import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.content.Intent
@@ -14,6 +14,7 @@ import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
+import com.kom.skyfly.R
import com.kom.skyfly.databinding.ActivityPaymentBinding
class PaymentActivity : AppCompatActivity() {
@@ -28,7 +29,8 @@ class PaymentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- btnCloseSnap = binding.btnClose
+ setHeaderTitle()
+ btnCloseSnap = binding.btnNext
btnCloseSnap!!.setOnClickListener(
View.OnClickListener { view: View? ->
transactionId?.let { openIssueTicketBottomSheets(it, adult, children, baby) }
@@ -45,6 +47,13 @@ class PaymentActivity : AppCompatActivity() {
}
}
+ private fun setHeaderTitle() {
+ binding.layoutHeader.tvTitleHeader.setText(R.string.text_payment)
+ binding.layoutHeader.ivBack.setOnClickListener {
+ onBackPressed()
+ }
+ }
+
@SuppressLint("SetJavaScriptEnabled")
private fun openUrlFromWebView(url: String?) {
val webView: WebView = binding.wvPayment
@@ -121,7 +130,7 @@ class PaymentActivity : AppCompatActivity() {
) {
if (!supportFragmentManager.isStateSaved) {
val bottomSheetFragment =
- BottomSheetsIssueTicket.newInstance(transactionId, adult, child, baby)
+ IssueTicketBottomSheets.newInstance(transactionId, adult, child, baby)
bottomSheetFragment.show(supportFragmentManager, bottomSheetFragment.tag)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/HistoryFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/history/HistoryFragment.kt
index ba94acf..12844c7 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/HistoryFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/HistoryFragment.kt
@@ -1,42 +1,49 @@
package com.kom.skyfly.presentation.history
+import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
-import androidx.lifecycle.map
import androidx.recyclerview.widget.LinearLayoutManager
import com.kom.skyfly.R
-import com.kom.skyfly.data.model.history.Data
+import com.kom.skyfly.core.BaseActivity
+import com.kom.skyfly.data.datasource.history.FlightCodeListener
import com.kom.skyfly.databinding.FragmentHistoryBinding
+import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.presentation.history.filterflighthistory.CalendarView
import com.kom.skyfly.presentation.history.flightdetailhistory.FlightDetailHistoryActivity
import com.kom.skyfly.presentation.history.searchflighthistory.SearchFlightHistoryFragment
import com.kom.skyfly.presentation.history.viewitems.DataItem
import com.kom.skyfly.presentation.history.viewitems.HeaderItem
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section
import org.koin.androidx.viewmodel.ext.android.viewModel
+import java.time.LocalDate
-class HistoryFragment : Fragment() {
+class HistoryFragment : Fragment(), FlightCodeListener, CalendarView.DateSelectedListener {
private lateinit var binding: FragmentHistoryBinding
+ private val adapter: GroupieAdapter by lazy { GroupieAdapter() }
+ private val limit: Int = 5000
+ private var startDate: String? = null
+ private var endDate: String? = null
- private val adapter: GroupieAdapter by lazy {
- GroupieAdapter()
- }
-
- private val historyViewModel: HistoryViewModel by viewModel()
+ private val homeViewModel: HistoryViewModel by viewModel()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
- binding = FragmentHistoryBinding.inflate(layoutInflater, container, false)
+ binding = FragmentHistoryBinding.inflate(inflater, container, false)
return binding.root
}
@@ -45,88 +52,171 @@ class HistoryFragment : Fragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
- getHistoryData()
- setRecyclerView()
setClickListener()
+ setupRecyclerView()
+ setupSearchButton()
+ getHistoryData(limit, null, null, null)
+ }
+
+ private fun setupRecyclerView() {
+ binding.rvPage.layoutManager = LinearLayoutManager(requireContext())
+ binding.rvPage.adapter = adapter
+ }
+
+ private fun setupSearchButton() {
+ binding.layoutHeaderOrderHistory.ivSearch.setOnClickListener {
+ showSearchFlightHistoryFragment()
+ }
+ }
+
+ private fun showSearchFlightHistoryFragment() {
+ val searchFlightHistoryFragment = SearchFlightHistoryFragment()
+ searchFlightHistoryFragment.setFlightCodeListener(this)
+ searchFlightHistoryFragment.show(parentFragmentManager, searchFlightHistoryFragment.tag)
}
- private fun getHistoryData() {
- historyViewModel.getHistoryData().observe(viewLifecycleOwner) { result ->
- result.proceedWhen(
- doOnLoading = {
- binding.layoutState.root.isVisible = true
- binding.shimmerHistory.isVisible = true
- binding.layoutState.tvError.isVisible = false
- binding.rvPage.isVisible = false
- },
- doOnSuccess = { data ->
- binding.layoutState.root.isVisible = false
- binding.shimmerHistory.isVisible = false
- binding.layoutState.tvError.isVisible = false
- binding.rvPage.isVisible = true
-
- data.payload?.forEach { sectionedDate ->
- val section =
- Section().apply {
- setHeader(
- HeaderItem(sectionedDate.date) { date ->
- Toast.makeText(
- requireContext(),
- "Header Clicked : $date",
- Toast.LENGTH_SHORT,
- ).show()
- },
- )
- val dataItems =
- sectionedDate.data.map { data ->
- DataItem(data) { clickedData ->
- navigateToDetail(clickedData)
+ override fun onFlightCodeEntered(flightCode: String) {
+ getHistoryData(limit, startDate, endDate, flightCode)
+ }
+
+ private fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ) {
+ homeViewModel.getHistoryData(limit, startDate, endDate, flightCode)
+ .observe(viewLifecycleOwner) { result ->
+ result.proceedWhen(
+ doOnLoading = {
+ binding.main.isRefreshing = true
+ binding.shimmerHistory.isVisible = true
+ binding.rvPage.isVisible = false
+ binding.csvHistory.isVisible = false
+ },
+ doOnSuccess = { data ->
+ binding.main.isRefreshing = false
+ binding.shimmerHistory.isVisible = false
+ binding.rvPage.isVisible = true
+ binding.csvHistory.setState(ContentState.SUCCESS)
+
+ if (result.payload?.data == null || result.payload.data.isEmpty()) {
+ binding.csvHistory.setState(
+ ContentState.EMPTY,
+ getString(R.string.text_history_data_empty),
+ )
+ }
+
+ adapter.clear()
+
+ data.payload?.let { sectionedDate ->
+ sectionedDate.data.forEach { itemsHistoryDomain ->
+ val section =
+ Section().apply {
+ setHeader(
+ HeaderItem(itemsHistoryDomain.date) { date ->
+ Toast.makeText(
+ requireContext(),
+ "Header Clicked: $date",
+ Toast.LENGTH_SHORT,
+ ).show()
+ },
+ )
+
+ val uniqueItemsMap = LinkedHashMap()
+ val sortedTransactions =
+ itemsHistoryDomain.transactions.sortedByDescending { it.id }
+
+ sortedTransactions.forEach { transaction ->
+ val dataItem =
+ DataItem(transaction) { clickedData, transactionId ->
+ navigateToDetail(transactionId)
+ }
+
+ val key = transaction.id
+ if (!uniqueItemsMap.containsKey(key)) {
+ uniqueItemsMap[key] = dataItem
+ }
+ }
+
+ uniqueItemsMap.forEach {
+ add(it.value)
}
}
- addAll(dataItems)
+ adapter.add(section)
}
- adapter.add(section)
- }
- },
- doOnError = { error ->
- Toast.makeText(requireContext(), "Error: ${error.message}", Toast.LENGTH_SHORT)
- .show()
- },
- doOnEmpty = {
- binding.layoutState.root.isVisible = true
- binding.shimmerHistory.isVisible = false
- binding.layoutState.ivError.isVisible = true
- binding.layoutState.tvTitleError.isVisible = true
- binding.layoutState.tvTitleError.text = getString(R.string.text_history_data_empty)
- binding.layoutState.tvError.isVisible = true
- binding.layoutState.tvError.text = getString(R.string.text_havent_made_a_booking)
- binding.rvPage.isVisible = false
- },
- )
- }
- }
+ }
+ },
+ doOnError = { error ->
+ binding.main.isRefreshing = false
+ binding.shimmerHistory.isVisible = false
- private fun setRecyclerView() {
- binding.rvPage.apply {
- layoutManager = LinearLayoutManager(requireContext())
- adapter = this@HistoryFragment.adapter
- }
+ if (error.exception is NoInternetException) {
+ binding.csvHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ (activity as BaseActivity).errorHandler(error.exception)
+ binding.csvHistory.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ (activity as BaseActivity).errorHandler(error.exception)
+ binding.csvHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvHistory.setState(ContentState.ERROR_GENERAL)
+ }
+ Log.e("HistoryFragment", "Error: ${error.exception?.message}")
+ },
+ doOnEmpty = {
+ binding.main.isRefreshing = false
+ binding.shimmerHistory.isVisible = false
+ binding.csvHistory.setState(
+ ContentState.EMPTY,
+ getString(R.string.text_history_data_empty),
+ )
+ },
+ )
+ }
}
private fun setClickListener() {
binding.clFilter.setOnClickListener {
val calendarView = CalendarView()
+ calendarView.setDateSelectedListener(this)
calendarView.show(parentFragmentManager, calendarView.tag)
}
binding.layoutHeaderOrderHistory.ivSearch.setOnClickListener {
val searchHistory = SearchFlightHistoryFragment()
searchHistory.show(parentFragmentManager, searchHistory.tag)
}
- binding.rvPage.setOnClickListener {
+ binding.main.setOnRefreshListener {
+ startDate = null
+ endDate = null
+ getHistoryData(limit, null, null, null)
}
}
- private fun navigateToDetail(item: Data) {
- FlightDetailHistoryActivity.startActivity(requireContext(), item)
+ private fun navigateToDetail(transactionId: String?) {
+ val intent =
+ Intent(requireContext(), FlightDetailHistoryActivity::class.java).apply {
+ putExtra("EXTRAS_TRANSACTION_ID", transactionId)
+ }
+ startActivity(intent)
+ }
+
+ override fun onDateSelected(
+ startDate: LocalDate?,
+ endDate: LocalDate?,
+ ) {
+ this.startDate = startDate?.toString()
+ this.endDate = endDate?.toString()
+ getHistoryData(limit, this.startDate, this.endDate, null)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/HistoryViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/history/HistoryViewModel.kt
index aadd831..1997cc3 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/HistoryViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/HistoryViewModel.kt
@@ -8,5 +8,11 @@ import kotlinx.coroutines.Dispatchers
class HistoryViewModel(
private val historyRepository: HistoryRepository,
) : ViewModel() {
- fun getHistoryData() = historyRepository.getHistoryData().asLiveData(Dispatchers.IO)
+ fun getHistoryData(
+ limit: Int?,
+ startDate: String?,
+ endDate: String?,
+ flightCode: String?,
+ ) = historyRepository.getHistoryData(limit, startDate, endDate, flightCode)
+ .asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/filterflighthistory/CalendarView.kt b/app/src/main/java/com/kom/skyfly/presentation/history/filterflighthistory/CalendarView.kt
index 26162ad..63d40b5 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/filterflighthistory/CalendarView.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/filterflighthistory/CalendarView.kt
@@ -34,6 +34,19 @@ class CalendarView : BottomSheetDialogFragment() {
private val dateFormatter = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy", Locale("id", "ID"))
+ fun setDateSelectedListener(listener: DateSelectedListener) {
+ dateSelectedListener = listener
+ }
+
+ private var dateSelectedListener: DateSelectedListener? = null
+
+ interface DateSelectedListener {
+ fun onDateSelected(
+ startDate: LocalDate?,
+ endDate: LocalDate?,
+ )
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -50,10 +63,7 @@ class CalendarView : BottomSheetDialogFragment() {
super.onViewCreated(view, savedInstanceState)
setOnClickListener()
- val maxPeekHeight =
- resources.getDimensionPixelSize(
- R.dimen.max_bottom_sheet_height,
- )
+ val maxPeekHeight = resources.getDimensionPixelSize(R.dimen.max_bottom_sheet_height)
setBottomSheetMaxHeight(maxPeekHeight)
val daysOfWeek = DayOfWeek.values()
@@ -110,7 +120,6 @@ class CalendarView : BottomSheetDialogFragment() {
textView.visibility = View.INVISIBLE
}
}
-
textView.alpha = if (data.position == DayPosition.MonthDate) 1f else 0.3f
}
}
@@ -151,15 +160,22 @@ class CalendarView : BottomSheetDialogFragment() {
binding.ivPreviousMonth.setOnClickListener {
binding.calendarView.smoothScrollToMonth(
- binding.calendarView.findFirstVisibleMonth()?.yearMonth?.minusMonths(1) ?: YearMonth.now().minusMonths(1),
+ binding.calendarView.findFirstVisibleMonth()?.yearMonth?.minusMonths(1)
+ ?: YearMonth.now().minusMonths(1),
)
}
binding.ivNextMonth.setOnClickListener {
binding.calendarView.smoothScrollToMonth(
- binding.calendarView.findFirstVisibleMonth()?.yearMonth?.plusMonths(1) ?: YearMonth.now().plusMonths(1),
+ binding.calendarView.findFirstVisibleMonth()?.yearMonth?.plusMonths(1)
+ ?: YearMonth.now().plusMonths(1),
)
}
+
+ binding.btnSave.setOnClickListener {
+ dateSelectedListener?.onDateSelected(startDate, endDate)
+ dismiss()
+ }
}
private fun setOnClickListener() {
@@ -191,7 +207,7 @@ class CalendarView : BottomSheetDialogFragment() {
}
inner class DayViewContainer(view: View) : ViewContainer(view) {
- val textView = view.findViewById(R.id.calendarDayText)
+ val textView: TextView = view.findViewById(R.id.calendarDayText)
lateinit var day: CalendarDay
init {
@@ -212,6 +228,6 @@ class CalendarView : BottomSheetDialogFragment() {
}
inner class MonthViewContainer(view: View) : ViewContainer(view) {
- val titlesContainer = view as ViewGroup
+ val titlesContainer: ViewGroup = view as ViewGroup
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryActivity.kt
index f1380e4..9aa8d01 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryActivity.kt
@@ -1,90 +1,316 @@
package com.kom.skyfly.presentation.history.flightdetailhistory
-import android.content.Context
-import android.content.Intent
+import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import coil.load
import com.kom.skyfly.R
-import com.kom.skyfly.data.model.history.Data
+import com.kom.skyfly.core.BaseActivity
+import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
+import com.kom.skyfly.data.model.transaction.paymentstatus.ItemsPaymentStatus
import com.kom.skyfly.databinding.ActivityFlightDetailHistoryBinding
+import com.kom.skyfly.presentation.common.views.ContentState
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
+import com.kom.skyfly.utils.formatToRupiah
+import com.kom.skyfly.utils.proceedWhen
+import es.dmoral.toasty.Toasty
import org.koin.androidx.viewmodel.ext.android.viewModel
-import org.koin.core.parameter.parametersOf
-
-class FlightDetailHistoryActivity : AppCompatActivity() {
- // private lateinit var currentHistory: HistoryDetail
+class FlightDetailHistoryActivity : BaseActivity() {
private val binding: ActivityFlightDetailHistoryBinding by lazy {
ActivityFlightDetailHistoryBinding.inflate(layoutInflater)
}
- private val detailHistoryViewModel: FlightDetailHistoryViewModel by viewModel {
- parametersOf(intent.extras)
- }
+ private val detailHistoryViewModel: FlightDetailHistoryViewModel by viewModel()
+ private var transactionIdFromExtras: String? = null
+ private var historyDetails: TransactionDetailResponses? = null
+ private var vaNumbers: ItemsPaymentStatus? = null
+ private var orderId: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
-
-// val historyId = intent.getStringExtra(EXTRAS_MOVIE)
-// if (historyId != null) {
-// setupBind(historyId)
-// } else {
-// // Handle jika bookingCode tidak tersedia
-// }
- detailHistoryViewModel.history?.let { setupBind(it) }
-
+ transactionIdFromExtras = intent.getStringExtra("EXTRAS_TRANSACTION_ID")
setOnClickListeners()
+ observeData(transactionIdFromExtras)
}
private fun setOnClickListeners() {
binding.ivBack.setOnClickListener {
onBackPressed()
}
+ binding.main.setOnRefreshListener {
+ observeData(transactionIdFromExtras)
+ }
+ binding.btnCancelTransaction.setOnClickListener {
+ confirmCancelTransaction()
+ }
}
- companion object {
- const val EXTRAS_HISTORY = "EXTRAS_HISTORY"
+ private fun confirmCancelTransaction() {
+ val dialog =
+ AlertDialog.Builder(this)
+ .setMessage(getString(R.string.text_are_you_sure_you_want_to_cancel_transaction))
+ .setPositiveButton(
+ getString(R.string.text_yes),
+ ) { dialog, id ->
+ doCancelTransaction(orderId)
+ }
+ .setNegativeButton(
+ getString(R.string.text_no),
+ ) { dialog, id ->
+ }.create()
+ dialog.show()
+ }
- fun startActivity(
- context: Context,
- data: Data,
- ) {
- val intent = Intent(context, FlightDetailHistoryActivity::class.java)
- intent.putExtra(EXTRAS_HISTORY, data)
- context.startActivity(intent)
+ private fun doCancelTransaction(orderId: String) {
+ detailHistoryViewModel.cancelTransaction(orderId).observe(this) { cancelResult ->
+ cancelResult.proceedWhen(
+ doOnSuccess = {
+ binding.pbLoading.isVisible = false
+ Toasty.success(this, "Transaction Cancelled!", Toast.LENGTH_SHORT).show()
+ observeData(transactionIdFromExtras)
+ },
+ doOnLoading = {
+ binding.btnCancelTransaction.isEnabled = false
+ binding.pbLoading.isVisible = true
+ },
+ doOnError = { error ->
+ if (error.exception is NoInternetException) {
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_GENERAL,
+ getString(R.string.text_something_went_wrong),
+ )
+ }
+ },
+ )
}
}
-// private fun observeData(historyId: String) {
-// detailHistoryViewModel.getDetailHistoryById(historyId).observe(this) { result ->
-// result.proceedWhen(
-// doOnSuccess = {
-// it.payload?.let { historyDetail ->
-// currentHistory = historyDetail
-// setupBind(historyDetail)
-// }
-// },
-// )
-// }
-// }
-
- private fun setupBind(historyDetail: Data) {
+ private fun observePaymentStatus(id: String?) {
+ if (id != null) {
+ detailHistoryViewModel.getPaymentStatus(id)
+ .observe(this) { resultStatus ->
+ resultStatus.proceedWhen(
+ doOnSuccess = {
+ vaNumbers = resultStatus.payload
+ setupBind(historyDetails, vaNumbers)
+ },
+ doOnError = { error ->
+ vaNumbers = null
+ },
+ doOnEmpty = {
+ vaNumbers = null
+ },
+ )
+ }
+ }
+ }
+
+ private fun observeData(id: String?) {
+ if (id != null) {
+ detailHistoryViewModel.getHistoryById(id).observe(this) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ binding.main.isRefreshing = false
+ binding.shimmerDetailHistory.isVisible = false
+ binding.svFlightDetails.isVisible = true
+ historyDetails = it.payload
+ orderId = it.payload?.data?.orderId.toString()
+ observePaymentStatus(orderId)
+ if (vaNumbers == null) {
+ setupBind(historyDetails, null)
+ }
+ },
+ doOnLoading = {
+ binding.shimmerDetailHistory.isVisible = true
+ binding.svFlightDetails.isVisible = false
+ binding.btnCancelTransaction.isEnabled = false
+ binding.main.isRefreshing = true
+ },
+ doOnError = { error ->
+ binding.main.isRefreshing = false
+ if (error.exception is NoInternetException) {
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvFlightDetailsHistory.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvFlightDetailsHistory.setState(ContentState.ERROR_GENERAL)
+ }
+ },
+ doOnEmpty = {
+ binding.main.isRefreshing = false
+ binding.shimmerDetailHistory.isVisible = false
+ getString(R.string.text_havent_made_a_booking)
+ binding.svFlightDetails.isVisible = false
+ binding.btnCancelTransaction.isVisible = false
+ },
+ )
+ }
+ }
+ }
+
+ @SuppressLint("StringFormatMatches")
+ private fun setupBind(
+ historyDetail: TransactionDetailResponses?,
+ itemPaymentStatus: ItemsPaymentStatus?,
+ ) {
+ val transactionDetails = historyDetail?.data?.transactionDetails
+ val paymentStatus = historyDetail?.data?.status
+
+ var adultCount = 0
+ var childCount = 0
+ var babyCount = 0
+ var totalPricePassengerAdult = 0
+ var totalPricePassengerChild = 0
+ var totalPricePassengerBaby = 0
+ var airlineImage: String? = null
+ historyDetail?.data?.transactionDetails?.map {
+ airlineImage = it?.flight?.airline?.image
+ }
+
+ transactionDetails?.forEach { detail ->
+ when (detail?.passengerCategory) {
+ "ADULT" -> {
+ adultCount++
+ totalPricePassengerAdult += detail.totalPrice
+ }
+
+ "CHILD" -> {
+ childCount++
+ totalPricePassengerChild += detail.totalPrice
+ }
+
+ "INFRANT" -> {
+ babyCount++
+ totalPricePassengerBaby += detail.totalPrice
+ }
+ }
+ }
+ binding.layoutFlightDetails.tvTitlePassenger.text = getString(R.string.text_empty)
+ binding.layoutFlightDetails.tvCitizenship.text = getString(R.string.text_empty)
+
with(binding.layoutFlightDetails) {
- tvPaymentStatus.text = historyDetail.status
- tvDetailDepartureDate.text = historyDetail.departureDate
- tvDetailDepartureTime.text = historyDetail.departureTime
- tvDetailArrivalDate.text = historyDetail.arrivalDate
- tvDetailArrivalTime.text = historyDetail.arrivalTime
- tvDetailBookingCode.text = historyDetail.bookingCode
- tvDetailClass.text = historyDetail.flightClass
- tvTotalPrice.text = historyDetail.total
-
- when (historyDetail.status) {
- "Unpaid" -> {
- tvPaymentStatus.background =
- ContextCompat.getDrawable(tvPaymentStatus.context, R.drawable.btn_rounded)
+ ivAirline.load(
+ airlineImage,
+ ) {
+ error(R.drawable.img_airline)
+ }
+ tvPaymentStatus.text = paymentStatus
+ tvDetailBookingCode.text = historyDetail?.data?.booking?.code
+ tvDetailDepartureDate.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.departure?.date
+ tvDetailDepartureTime.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.departure?.time
+ tvDetailDepartureAirport.text =
+ "${historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.departureAirport?.name} - ${historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.airline?.terminal}"
+ tvDetailAirline.text =
+ getString(
+ R.string.tv_strip,
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.airline?.name,
+ )
+ tvDetailClass.text = historyDetail?.data?.transactionDetails?.firstOrNull()?.seat?.type
+ tvDetailFlightNumber.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.code
+ tvDetailArrivalDate.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.arrival?.date
+ tvDetailArrivalTime.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.arrival?.time
+ tvDetailDestinationAirport.text =
+ historyDetail?.data?.transactionDetails?.firstOrNull()?.flight?.destinationAirport?.name
+ tvTotalPrice.text = historyDetail?.data?.totalPrice?.formatToRupiah().toString()
+ tvTax.text = historyDetail?.data?.tax?.formatToRupiah().toString()
+
+ if (adultCount > 0) {
+ tvTotalByAgeGroupAdult.text = getString(R.string.text_adult, adultCount)
+ tvTotalPriceByAgeGroupAdult.text =
+ totalPricePassengerAdult.formatToRupiah().toString()
+ tvTotalByAgeGroupAdult.isVisible = true
+ tvTotalPriceByAgeGroupAdult.isVisible = true
+ } else {
+ tvTotalByAgeGroupAdult.isVisible = false
+ tvTotalPriceByAgeGroupAdult.isVisible = false
+ }
+
+ if (childCount > 0) {
+ tvTotalByAgeGroupChild.text = getString(R.string.text_child, childCount)
+ tvTotalPriceByAgeGroupChild.text =
+ totalPricePassengerChild.formatToRupiah().toString()
+ tvTotalByAgeGroupChild.isVisible = true
+ tvTotalPriceByAgeGroupChild.isVisible = true
+ } else {
+ tvTotalByAgeGroupChild.isVisible = false
+ tvTotalPriceByAgeGroupChild.isVisible = false
+ }
+
+ if (babyCount > 0) {
+ tvTotalByAgeGroupBaby.text = getString(R.string.text_baby, babyCount)
+ tvTotalPriceByAgeGroupBaby.text =
+ totalPricePassengerBaby.formatToRupiah().toString()
+ tvTotalByAgeGroupBaby.isVisible = true
+ tvTotalPriceByAgeGroupBaby.isVisible = true
+ } else {
+ tvTotalByAgeGroupBaby.isVisible = false
+ tvTotalPriceByAgeGroupBaby.isVisible = false
+ }
+
+ transactionDetails?.forEachIndexed { index, detail ->
+ val passengerName = detail?.name ?: "Unknown"
+ val citizenship = detail?.citizenship ?: "Unknown"
+
+ tvTitlePassenger.append(
+ "Passenger ${index + 1} : $passengerName\n" +
+ "Citizenship : $citizenship\n",
+ )
+ }
+
+ when {
+ paymentStatus.equals("pending", ignoreCase = true) -> {
+ tvPaymentStatus.setText(R.string.text_unpaid)
+ tvVaNumber.text =
+ itemPaymentStatus?.vaNumbers?.joinToString { it.vaNumber.toString() }
+ ?: "N/A"
+
+ tvPaymentCodeTitle.isVisible = true
+ tvVaNumber.isVisible = true
tvPaymentStatus.backgroundTintList =
ColorStateList.valueOf(
ContextCompat.getColor(
@@ -94,32 +320,43 @@ class FlightDetailHistoryActivity : AppCompatActivity() {
)
}
- "Cancelled" -> {
- tvPaymentStatus.background =
- ContextCompat.getDrawable(tvPaymentStatus.context, R.drawable.btn_rounded)
+ paymentStatus.equals("settlement", ignoreCase = true) -> {
+ tvPaymentStatus.setText(R.string.text_paid)
tvPaymentStatus.backgroundTintList =
ColorStateList.valueOf(
ContextCompat.getColor(
tvPaymentStatus.context,
- R.color.darkGrey,
+ R.color.green,
),
)
}
- "Paid" -> {
- tvPaymentStatus.background =
- ContextCompat.getDrawable(tvPaymentStatus.context, R.drawable.btn_rounded)
+ else -> {
+ tvPaymentStatus.setText(R.string.text_cancelled)
tvPaymentStatus.backgroundTintList =
ColorStateList.valueOf(
ContextCompat.getColor(
tvPaymentStatus.context,
- R.color.green,
+ R.color.darkGrey,
),
)
}
+ }
- else -> {
- tvPaymentStatus.background = null
+ when {
+ paymentStatus.equals("pending", ignoreCase = true) -> {
+ binding.btnCancelTransaction.text = getString(R.string.text_cancel_transaction)
+ binding.btnCancelTransaction.isEnabled = true
+ }
+
+ paymentStatus.equals("settlement", ignoreCase = true) -> {
+ binding.btnCancelTransaction.text = getString(R.string.text_payment_complete)
+ binding.btnCancelTransaction.isEnabled = false
+ }
+
+ paymentStatus.equals("expired", ignoreCase = true) -> {
+ binding.btnCancelTransaction.text = getString(R.string.text_payment_cancelled)
+ binding.btnCancelTransaction.isEnabled = false
}
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryViewModel.kt
index 3af7269..743a72b 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/flightdetailhistory/FlightDetailHistoryViewModel.kt
@@ -1,15 +1,23 @@
package com.kom.skyfly.presentation.history.flightdetailhistory
-import android.os.Bundle
import androidx.lifecycle.ViewModel
-import com.kom.skyfly.data.model.history.Data
-import com.kom.skyfly.data.repository.history.HistoryRepository
+import androidx.lifecycle.asLiveData
+import com.kom.skyfly.data.repository.transaction.TransactionRepository
+import kotlinx.coroutines.Dispatchers
class FlightDetailHistoryViewModel(
- private val extras: Bundle?,
- private val historyRepository: HistoryRepository,
+ private val transactionRepository: TransactionRepository,
) : ViewModel() {
- val history = extras?.getParcelable(FlightDetailHistoryActivity.EXTRAS_HISTORY)
+ fun getHistoryById(id: String) =
+ transactionRepository
+ .getTransactionById(id)
+ .asLiveData(Dispatchers.IO)
-// fun getDetailHistoryById(id: String) = historyRepository.getDetailHistoryById(id.toInt()).asLiveData(Dispatchers.IO)
+ fun getPaymentStatus(id: String) =
+ transactionRepository.getPaymentStatus(id)
+ .asLiveData(Dispatchers.IO)
+
+ fun cancelTransaction(id: String) =
+ transactionRepository.cancelTransaction(id)
+ .asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryFragment.kt
index 4d98c0a..2633b53 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryFragment.kt
@@ -13,24 +13,23 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.kom.skyfly.R
+import com.kom.skyfly.data.datasource.history.FlightCodeListener
import com.kom.skyfly.data.model.searchhistory.SearchHistory
import com.kom.skyfly.databinding.FragmentSearchFlightHistoryBinding
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.presentation.history.searchflighthistory.adapter.SearchHistoryListAdapter
import com.kom.skyfly.presentation.history.searchflighthistory.adapter.SearchHistoryListener
-import com.kom.skyfly.utils.hideKeyboard
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.viewModel
class SearchFlightHistoryFragment : BottomSheetDialogFragment() {
private lateinit var binding: FragmentSearchFlightHistoryBinding
-
private val searchFlightHistoryViewModel: SearchFlightHistoryViewModel by viewModel()
-
+ private var flightCodeListener: FlightCodeListener? = null
private val adapter: SearchHistoryListAdapter by lazy {
SearchHistoryListAdapter(
itemClick = { searchHistory ->
- // Handle item click here if needed
+ binding.layoutSearchBar.etSearch.setText(searchHistory.searchHistory)
},
searchHistoryListener =
object : SearchHistoryListener {
@@ -41,12 +40,16 @@ class SearchFlightHistoryFragment : BottomSheetDialogFragment() {
)
}
+ fun setFlightCodeListener(listener: FlightCodeListener) {
+ flightCodeListener = listener
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
- binding = FragmentSearchFlightHistoryBinding.inflate(layoutInflater, container, false)
+ binding = FragmentSearchFlightHistoryBinding.inflate(inflater, container, false)
return binding.root
}
@@ -59,10 +62,7 @@ class SearchFlightHistoryFragment : BottomSheetDialogFragment() {
getSearchHistoryData()
setList()
- val maxPeekHeight =
- resources.getDimensionPixelSize(
- R.dimen.max_bottom_sheet_height,
- )
+ val maxPeekHeight = resources.getDimensionPixelSize(R.dimen.max_bottom_sheet_height)
setBottomSheetMaxHeight(maxPeekHeight)
}
@@ -72,10 +72,8 @@ class SearchFlightHistoryFragment : BottomSheetDialogFragment() {
doOnSuccess = {
binding.rvPage.isVisible = true
binding.csvSearchHistory.setState(ContentState.SUCCESS)
- result.payload.let {
- if (it != null) {
- adapter.submitData(it)
- }
+ result.payload?.let {
+ adapter.submitData(it)
}
},
doOnError = {
@@ -113,28 +111,22 @@ class SearchFlightHistoryFragment : BottomSheetDialogFragment() {
binding.layoutSearchBar.etSearch.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
actionId == EditorInfo.IME_ACTION_DONE ||
- event != null &&
- event.action == KeyEvent.ACTION_DOWN &&
- event.keyCode == KeyEvent.KEYCODE_ENTER
+ event != null && event.action == KeyEvent.ACTION_DOWN && event.keyCode == KeyEvent.KEYCODE_ENTER
) {
val searchHistory = binding.layoutSearchBar.etSearch.text.toString()
- observeData("39483943tfeff", searchHistory)
- binding.layoutSearchBar.etSearch.clearFocus()
- hideKeyboard()
+
+ flightCodeListener?.onFlightCodeEntered(searchHistory)
+ observeData("gejefg", searchHistory)
+ dismiss()
return@setOnEditorActionListener true
}
- return@setOnEditorActionListener false
- }
- binding.tvDeleteRecentSearches.setOnClickListener {
- searchFlightHistoryViewModel.deleteAllSearchHistory()
- Toast.makeText(requireContext(), "deleteall", Toast.LENGTH_SHORT).show()
+ false
}
}
private fun setList() {
- val layoutManager = LinearLayoutManager(requireContext())
- binding.rvPage.layoutManager = layoutManager
- binding.rvPage.adapter = this@SearchFlightHistoryFragment.adapter
+ binding.rvPage.layoutManager = LinearLayoutManager(requireContext())
+ binding.rvPage.adapter = adapter
}
private fun observeData(
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryViewModel.kt
index 2e25f97..6cf3ed2 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/searchflighthistory/SearchFlightHistoryViewModel.kt
@@ -11,10 +11,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
-/**
-Written by Komang Yuda Saputra
-Github : https://github.com/YudaSaputraa
- **/
class SearchFlightHistoryViewModel(private val searchHistoryRepository: SearchHistoryRepository) :
ViewModel() {
fun getAllSearchHistory() = searchHistoryRepository.getUserSearchHistory().asLiveData(Dispatchers.IO)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/history/viewitems/Items.kt b/app/src/main/java/com/kom/skyfly/presentation/history/viewitems/Items.kt
index fdc3c51..2c341aa 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/history/viewitems/Items.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/history/viewitems/Items.kt
@@ -4,13 +4,16 @@ import android.content.res.ColorStateList
import android.view.View
import androidx.core.content.ContextCompat
import com.kom.skyfly.R
-import com.kom.skyfly.data.model.history.Data
+import com.kom.skyfly.data.model.history.new.TransactionDomain
import com.kom.skyfly.databinding.ItemOrderHistoryBinding
import com.kom.skyfly.databinding.ItemSectionHeaderHistoryBinding
+import com.kom.skyfly.utils.formatToRupiah
import com.xwray.groupie.viewbinding.BindableItem
-class HeaderItem(private val title: String, private val onHeaderClick: (item: String) -> Unit) :
- BindableItem() {
+class HeaderItem(
+ private val title: String,
+ private val onHeaderClick: (item: String) -> Unit,
+) : BindableItem() {
override fun bind(
viewBinding: ItemSectionHeaderHistoryBinding,
position: Int,
@@ -21,71 +24,90 @@ class HeaderItem(private val title: String, private val onHeaderClick: (item: St
override fun getLayout(): Int = R.layout.item_section_header_history
- override fun initializeViewBinding(view: View): ItemSectionHeaderHistoryBinding = ItemSectionHeaderHistoryBinding.bind(view)
+ override fun initializeViewBinding(view: View): ItemSectionHeaderHistoryBinding {
+ return ItemSectionHeaderHistoryBinding.bind(view)
+ }
}
-class DataItem(private val data: Data, private val onItemClick: (item: Data) -> Unit) :
+class DataItem(
+ private val data: TransactionDomain,
+ private val onItemClick: (item: TransactionDomain, transactionId: String?) -> Unit,
+) :
BindableItem() {
+ var paymentStatus: String? = null
+ var departureCity: String? = null
+ var departureDate: String? = null
+ var departureTime: String? = null
+ var flightDuration: String? = null
+ var destinationCity: String? = null
+ var arrivalDate: String? = null
+ var arrivalTime: String? = null
+ var bookingCode: String? = null
+ var seatClass: String? = null
+ var totalPrice: String? = null
+ var transactionId: String? = null
+
override fun bind(
viewBinding: ItemOrderHistoryBinding,
position: Int,
) {
+ paymentStatus = data.status
+ departureCity = data.transactionDetail.first().flight.departureAirport.city
+ departureDate = data.transactionDetail.first().flight.departure.date
+ departureTime = data.transactionDetail.first().flight.departure.time
+ flightDuration = data.transactionDetail.first().flight.flightDuration
+ destinationCity = data.transactionDetail.first().flight.destinationAirport.city
+ arrivalDate = data.transactionDetail.first().flight.arrival.date
+ arrivalTime = data.transactionDetail.first().flight.arrival.time
+ bookingCode = data.booking.code
+ seatClass = data.transactionDetail.first().seat.type
+ totalPrice = data.totalPrice.formatToRupiah().toString()
+ transactionId = data.id
+
with(viewBinding) {
- tvStatus.text = data.status
- tvDepartureLocation.text = data.departureLocation
- tvDepartureDate.text = data.departureDate
- tvDepartureTime.text = data.departureTime
- tvFlightDuration.text = data.flightDuration
- tvDestination.text = data.destination
- tvArrivalDate.text = data.arrivalDate
- tvArrivalTime.text = data.arrivalTime
- tvBookingCode.text = data.bookingCode
- tvClass.text = data.flightClass
- tvHistoryTotal.text = data.total
+ tvStatus.text = paymentStatus
+ tvDepartureLocation.text = departureCity
+ tvDepartureDate.text = departureDate
+ tvDepartureTime.text = departureTime
+ tvFlightDuration.text = flightDuration
+ tvDestination.text = destinationCity
+ tvArrivalDate.text = arrivalDate
+ tvArrivalTime.text = arrivalTime
+ tvBookingCode.text = bookingCode
+ tvClass.text = seatClass
+ tvHistoryTotal.text = totalPrice
viewBinding.root.setOnClickListener {
- onItemClick(data)
+ onItemClick(data, transactionId)
}
- when (data.status) {
- "Unpaid" -> {
- tvStatus.background =
- ContextCompat.getDrawable(tvStatus.context, R.drawable.btn_rounded)
- tvStatus.backgroundTintList =
- ColorStateList.valueOf(
- ContextCompat.getColor(
- tvStatus.context,
- R.color.red,
- ),
- )
- }
-
- "Cancelled" -> {
- tvStatus.background =
- ContextCompat.getDrawable(tvStatus.context, R.drawable.btn_rounded)
- tvStatus.backgroundTintList =
- ColorStateList.valueOf(
- ContextCompat.getColor(
- tvStatus.context,
- R.color.darkGrey,
- ),
- )
- }
-
- "Paid" -> {
- tvStatus.background =
- ContextCompat.getDrawable(tvStatus.context, R.drawable.btn_rounded)
- tvStatus.backgroundTintList =
- ColorStateList.valueOf(
- ContextCompat.getColor(
- tvStatus.context,
- R.color.green,
- ),
- )
- }
- else -> {
- tvStatus.background = null
- }
+ if (paymentStatus.equals("pending", ignoreCase = true)) {
+ tvStatus.setText(R.string.text_unpaid)
+ tvStatus.backgroundTintList =
+ ColorStateList.valueOf(
+ ContextCompat.getColor(
+ tvStatus.context,
+ R.color.red,
+ ),
+ )
+ } else if (paymentStatus.equals("settlement", ignoreCase = true)) {
+ tvStatus.setText(R.string.text_paid)
+ tvStatus.backgroundTintList =
+ ColorStateList.valueOf(
+ ContextCompat.getColor(
+ tvStatus.context,
+ R.color.green,
+ ),
+ )
+ } else if (paymentStatus.equals("expire", ignoreCase = true) || paymentStatus.equals("cancel", ignoreCase = true)) {
+ tvStatus.setText(R.string.text_cancelled)
+ tvStatus.backgroundTintList =
+ ColorStateList.valueOf(
+ ContextCompat.getColor(
+ tvStatus.context,
+ R.color.darkGrey,
+ ),
+ )
}
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/HomeFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/home/HomeFragment.kt
index edfdaf7..0669d0f 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/HomeFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/HomeFragment.kt
@@ -1,5 +1,6 @@
package com.kom.skyfly.presentation.home
+import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@@ -8,7 +9,7 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.kom.skyfly.R
-import com.kom.skyfly.data.model.destinationfavorite.DestinationFavorite
+import com.kom.skyfly.data.model.home.destination_favourite.DestinationFavourite
import com.kom.skyfly.databinding.FragmentHomeBinding
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.presentation.home.adapter.DestinationFavoriteAdapter
@@ -16,11 +17,11 @@ import com.kom.skyfly.presentation.home.calendar.HomeCalendarFragment
import com.kom.skyfly.presentation.home.passenger.PassengerFragment
import com.kom.skyfly.presentation.home.search.SearchFragment
import com.kom.skyfly.presentation.home.search_result.SearchResultActivity
+import com.kom.skyfly.presentation.home.seatclass.SeatClassFragment
import com.kom.skyfly.presentation.main.MainViewModel
import com.kom.skyfly.utils.NoInternetException
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.activityViewModel
-import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@@ -31,9 +32,20 @@ class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private val homeViewModel: HomeViewModel by viewModel()
private val sharedViewModel: MainViewModel by activityViewModel()
- private val destinationAdapter: DestinationFavoriteAdapter by lazy { DestinationFavoriteAdapter {} }
private var source: String = ""
private var dest: String = ""
+ private var departureAirport: String? = null
+ private var arrivalAirport: String? = null
+ private val destinationAdapter: DestinationFavoriteAdapter by lazy {
+ DestinationFavoriteAdapter { item ->
+ item.let {
+ binding.layoutSelectDestination.tvStartFrom.text = it.departureCity
+ binding.layoutSelectDestination.tvEndDestination.text = it.arrivalCity
+ departureAirport = it.departureCity
+ arrivalAirport = it.arrivalCity
+ }
+ }
+ }
override fun onCreateView(
inflater: LayoutInflater,
@@ -57,50 +69,57 @@ class HomeFragment : Fragment() {
getDestinationFavoriteData()
}
+ @SuppressLint("SetTextI18n")
private fun observeDataDestination() {
sharedViewModel.sourceDestination.observe(viewLifecycleOwner) { destination ->
destination?.let {
if (sharedViewModel.isStartDestination!!) {
binding.layoutSelectDestination.tvStartFrom.text = it.city
source = binding.layoutSelectDestination.tvStartFrom.text.toString()
+ departureAirport = it.city
} else {
binding.layoutSelectDestination.tvEndDestination.text = it.city
dest = binding.layoutSelectDestination.tvEndDestination.text.toString()
+ arrivalAirport = it.city
}
}
+ validateFields()
}
sharedViewModel.startTime.observe(viewLifecycleOwner) { startTIme ->
startTIme?.let {
binding.tvDeparture.text = it
}
+ validateFields()
}
sharedViewModel.returnTime.observe(viewLifecycleOwner) { returnTime ->
returnTime?.let {
binding.tvReturn.text = it
}
+ validateFields()
}
sharedViewModel.passengerCountLiveData.observe(viewLifecycleOwner) { totalPassenger ->
totalPassenger?.let {
- binding.tvPassengers.text = it.toString()
+ binding.tvPassengers.text = "$it Passengers"
}
+ validateFields()
+ }
+ sharedViewModel.seatClass.observe(viewLifecycleOwner) { seatClass ->
+ seatClass?.let {
+ binding.tvSeats.text = it
+ }
+ validateFields()
}
}
- fun convertDateFormat(inputDate: String): String? {
- // Define the formatter for the input date format
+ private fun convertDateFormat(inputDate: String): String? {
val inputFormatter = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy", Locale("id", "ID"))
- // Define the formatter for the output date format
val outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
return try {
- // Parse the input date string to a LocalDate object
val date = LocalDate.parse(inputDate, inputFormatter)
-
- // Format the LocalDate object to the output format
date.format(outputFormatter)
} catch (e: DateTimeParseException) {
- // Handle the exception if the input date string is not in the expected format
e.printStackTrace()
null
}
@@ -132,15 +151,26 @@ class HomeFragment : Fragment() {
passengerBottomSheet.show(parentFragmentManager, passengerBottomSheet.tag)
}
binding.btnSearchFlight.setOnClickListener {
- val departureAirport = binding.layoutSelectDestination.tvStartFrom.text
- val arrivalAirport = binding.layoutSelectDestination.tvEndDestination.text
+ val babyCount = sharedViewModel.passengerBabyCountLiveData
+ val adultCount = sharedViewModel.passengerAdultCountLiveData
+ val childCount = sharedViewModel.passengerChildCountLiveData
+ val seatClass = sharedViewModel.seatClass
+ val roundTrip = sharedViewModel.roundTrip
+ val returnDate = convertDateFormat(binding.tvReturn.text.toString())
val departureTime = convertDateFormat(binding.tvDeparture.text.toString())
val intent =
Intent(requireContext(), SearchResultActivity::class.java).apply {
putExtra("EXTRA_DEPARTURE_AIRPORT", departureAirport)
putExtra("EXTRA_ARRIVAL_AIRPORT", arrivalAirport)
+ putExtra("EXTRA_RETURN_TIME", returnDate)
putExtra("EXTRA_DEPARTURE_TIME", departureTime)
+ putExtra("EXTRA_BABY_COUNT", babyCount.value)
+ putExtra("EXTRA_ADULT_COUNT", adultCount.value)
+ putExtra("EXTRA_CHILD_COUNT", childCount.value)
+ putExtra("EXTRA_SEAT_CLASS", seatClass.value)
+ putExtra("EXTRA_TOTAL_PASSENGER", sharedViewModel.passengerCountLiveData.value)
+ putExtra("EXTRA_ROUND_TRIP", roundTrip.value)
}
startActivity(intent)
}
@@ -151,6 +181,24 @@ class HomeFragment : Fragment() {
source = dest
dest = temp
}
+ binding.tvSeats.setOnClickListener {
+ val seatClassBottomSheet = SeatClassFragment()
+ seatClassBottomSheet.show(parentFragmentManager, seatClassBottomSheet.tag)
+ }
+ }
+
+ private fun validateFields() {
+ val isSourceValid = binding.layoutSelectDestination.tvStartFrom.text.toString().isNotEmpty()
+ val isDestinationValid =
+ binding.layoutSelectDestination.tvEndDestination.text.toString().isNotEmpty()
+ val isDepartureTimeValid = binding.tvDeparture.text.toString().isNotEmpty()
+ val isReturnTimeValid =
+ !binding.tvReturn.isEnabled || binding.tvReturn.text.toString().isNotEmpty()
+ val isPassengerCountValid = sharedViewModel.passengerCountLiveData.value != null
+ val isSeatClassValid = sharedViewModel.seatClass.value != null
+
+ binding.btnSearchFlight.isEnabled =
+ isSourceValid && isDestinationValid && isDepartureTimeValid && isReturnTimeValid && isPassengerCountValid && isSeatClassValid
}
private fun getDestinationFavoriteData() {
@@ -187,18 +235,24 @@ class HomeFragment : Fragment() {
private fun setRoundTrip() {
binding.btnSwitchRoundtrip.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
+ sharedViewModel.setRoundTrip(true)
binding.tvReturn.isEnabled = true
- binding.tvReturn.text = "Select Dates"
- binding.tvReturn.setTextColor(getResources().getColor(R.color.md_theme_primary))
+ binding.tvReturn.text = getString(R.string.text_select_dates)
+ val trackDrawable = binding.btnSwitchRoundtrip.trackDrawable
+ trackDrawable?.setTint(resources.getColor(R.color.md_theme_primaryFixed_mediumContrast))
+ binding.tvReturn.setTextColor(resources.getColor(R.color.md_theme_primary))
} else {
+ sharedViewModel.setRoundTrip(false)
binding.tvReturn.isEnabled = false
- binding.tvReturn.text = "-"
- binding.tvReturn.setTextColor(getResources().getColor(R.color.darkGrey))
+ binding.tvReturn.text = getString(R.string.text_strips)
+ binding.tvReturn.setTextColor(resources.getColor(R.color.darkGrey))
+ val trackDrawable = binding.btnSwitchRoundtrip.trackDrawable
+ trackDrawable?.setTint(resources.getColor(R.color.grey))
}
}
}
- private fun bindDestinationFavoriteList(data: List) {
+ private fun bindDestinationFavoriteList(data: List) {
destinationAdapter.submitData(data)
}
@@ -207,8 +261,4 @@ class HomeFragment : Fragment() {
adapter = destinationAdapter
}
}
-
- companion object {
- const val EXTRAS_DESTINATION = "EXTRAS_DESTINATION"
- }
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/HomeViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/home/HomeViewModel.kt
index 32c6f49..242db14 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/HomeViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/HomeViewModel.kt
@@ -2,8 +2,8 @@ package com.kom.skyfly.presentation.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
-import com.kom.skyfly.data.repository.destinationfavorite.DestinationFavoriteRepository
import com.kom.skyfly.data.repository.home.airport.AirportRepository
+import com.kom.skyfly.data.repository.home.destination_favourite.DestinationFavouriteRepository
import com.kom.skyfly.data.repository.home.flight_ticket.FlightTicketRepository
import com.kom.skyfly.data.repository.userpref.UserPrefRepository
import kotlinx.coroutines.Dispatchers
@@ -17,14 +17,14 @@ class HomeViewModel(
private val userPrefRepository: UserPrefRepository,
private val airportRepository: AirportRepository,
private val flightRepository: FlightTicketRepository,
- private val destinationFavoriteRepository: DestinationFavoriteRepository,
+ private val destinationFavoriteRepository: DestinationFavouriteRepository,
) : ViewModel() {
// val destination = extras?.getParcelable(HomeFragment.EXTRAS_DESTINATION)
fun setOnBoardingShow(isShown: Boolean) = userPrefRepository.setOnBoardingShow(isShown)
fun getAllDestinationFavorite() =
- destinationFavoriteRepository.getAllDestinationFavorite().asLiveData(
+ destinationFavoriteRepository.getAllDestinationFavourite().asLiveData(
Dispatchers.IO,
)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/adapter/DestinationFavoriteAdapter.kt b/app/src/main/java/com/kom/skyfly/presentation/home/adapter/DestinationFavoriteAdapter.kt
index f0c0c19..67997c2 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/adapter/DestinationFavoriteAdapter.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/adapter/DestinationFavoriteAdapter.kt
@@ -3,42 +3,42 @@ package com.kom.skyfly.presentation.home.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.core.view.isVisible
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.kom.skyfly.R
-import com.kom.skyfly.data.model.destinationfavorite.DestinationFavorite
+import com.kom.skyfly.data.model.home.destination_favourite.DestinationFavourite
import com.kom.skyfly.databinding.LayoutItemTicketGridBinding
+import com.kom.skyfly.utils.formatToRupiah
/**
Written by Komang Yuda Saputra
Github : https://github.com/YudaSaputraa
**/
-class DestinationFavoriteAdapter(private val itemClick: (DestinationFavorite) -> Unit) :
+class DestinationFavoriteAdapter(private val itemClick: (DestinationFavourite) -> Unit) :
RecyclerView.Adapter() {
private val dataDiffer =
AsyncListDiffer(
this,
- object : DiffUtil.ItemCallback() {
+ object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(
- oldItem: DestinationFavorite,
- newItem: DestinationFavorite,
+ oldItem: DestinationFavourite,
+ newItem: DestinationFavourite,
): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(
- oldItem: DestinationFavorite,
- newItem: DestinationFavorite,
+ oldItem: DestinationFavourite,
+ newItem: DestinationFavourite,
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
},
)
- fun submitData(data: List) {
+ fun submitData(data: List) {
dataDiffer.submitList(data)
}
@@ -62,31 +62,18 @@ class DestinationFavoriteAdapter(private val itemClick: (DestinationFavorite) ->
class ItemDestinationFavoriteViewHolder(
private val binding: LayoutItemTicketGridBinding,
- val itemClick: (DestinationFavorite) -> Unit,
+ val itemClick: (DestinationFavourite) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
- fun bindView(item: DestinationFavorite) {
+ fun bindView(item: DestinationFavourite) {
with(item) {
- binding.ivTicketImage.load(item.imageUrl) {
+ binding.ivTicketImage.load(item.img) {
crossfade(true)
error(R.mipmap.ic_launcher)
}
- val isLimited = item.isLimited
- val discount = item.discount
- if (isLimited == true) {
- binding.tvLimited.isVisible = true
- binding.tvLimited.text = "Limited"
- } else if (discount != null) {
- binding.tvLimited.isVisible = true
- binding.tvLimited.text = discount
- } else {
- binding.tvLimited.isVisible = false
- }
- binding.tvCardStartFrom.text = item.origin
- binding.tvCardDestination.text = "${item.origin} -> ${item.destination}"
- binding.tvCardDate.text = item.dateRange
+ binding.tvCardDestination.text = "${item.departureCity} -> ${item.arrivalCity}"
binding.tvCardAirplane.text = item.airline
- binding.tvCardPrice.text = item.price
+ binding.tvCardPrice.text = item.price.formatToRupiah()
itemView.setOnClickListener { itemClick(this) }
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/calendar/HomeCalendarFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/home/calendar/HomeCalendarFragment.kt
index 1d46012..61a8291 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/calendar/HomeCalendarFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/calendar/HomeCalendarFragment.kt
@@ -1,5 +1,6 @@
package com.kom.skyfly.presentation.home.calendar
+import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
@@ -73,6 +74,7 @@ class HomeCalendarFragment : BottomSheetDialogFragment() {
object : MonthDayBinder {
override fun create(view: View) = DayViewContainer(view)
+ @SuppressLint("SetTextI18n")
override fun bind(
container: DayViewContainer,
data: CalendarDay,
@@ -85,32 +87,39 @@ class HomeCalendarFragment : BottomSheetDialogFragment() {
DayPosition.MonthDate -> {
textView.visibility = View.VISIBLE
when {
+ startDate == data.date && (mainViewModel.roundTrip.value == false || endDate == data.date) -> {
+ textView.setTextColor(Color.WHITE)
+ textView.setBackgroundResource(R.drawable.selection_background)
+ binding.tvDepartureDate.text = data.date.format(dateFormatter)
+ mainViewModel.setStartTime(data.date.format(dateFormatter))
+ if (mainViewModel.roundTrip.value == true) {
+ binding.tvBackDate.text = data.date.format(dateFormatter)
+ } else {
+ binding.tvBackDate.text = getString(R.string.text_dash)
+ }
+ }
startDate == data.date -> {
textView.setTextColor(Color.WHITE)
textView.setBackgroundResource(R.drawable.selection_background)
binding.tvDepartureDate.text = data.date.format(dateFormatter)
mainViewModel.setStartTime(data.date.format(dateFormatter))
}
-
endDate == data.date -> {
textView.setTextColor(Color.WHITE)
textView.setBackgroundResource(R.drawable.selection_background)
binding.tvBackDate.text = data.date.format(dateFormatter)
mainViewModel.setReturnTime(data.date.format(dateFormatter))
}
-
startDate != null && endDate != null && (data.date > startDate && data.date < endDate) -> {
textView.setTextColor(Color.WHITE)
textView.setBackgroundResource(R.drawable.range_background)
}
-
else -> {
textView.setTextColor(Color.BLACK)
textView.background = null
}
}
}
-
else -> {
textView.visibility = View.INVISIBLE
}
@@ -204,13 +213,20 @@ class HomeCalendarFragment : BottomSheetDialogFragment() {
init {
view.setOnClickListener {
if (day.position == DayPosition.MonthDate) {
- if (startDate == null || endDate != null) {
- startDate = day.date
- endDate = null
- } else if (day.date < startDate) {
+ if (mainViewModel.roundTrip.value == false) {
+ // Single date selection logic
startDate = day.date
- } else {
endDate = day.date
+ } else {
+ // Range selection logic
+ if (startDate == null || endDate != null) {
+ startDate = day.date
+ endDate = null
+ } else if (day.date < startDate) {
+ startDate = day.date
+ } else {
+ endDate = day.date
+ }
}
binding.calendarView.notifyCalendarChanged()
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeActivity.kt
index a98f177..91d4383 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeActivity.kt
@@ -1,57 +1,227 @@
package com.kom.skyfly.presentation.home.detail_home
import android.annotation.SuppressLint
+import android.content.Intent
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
+import android.view.View
+import android.widget.ScrollView
+import androidx.core.view.isVisible
+import coil.load
+import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
+import com.kom.skyfly.data.model.home.intent.SearchResultIntent
import com.kom.skyfly.databinding.ActivityDetailHomeBinding
+import com.kom.skyfly.presentation.checkout.bookersbiodata.BookersBiodataActivity
+import com.kom.skyfly.presentation.common.views.ContentState
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
+import com.xwray.groupie.GroupieAdapter
import org.koin.androidx.viewmodel.ext.android.viewModel
-class DetailHomeActivity : AppCompatActivity() {
+class DetailHomeActivity : BaseActivity() {
private val binding: ActivityDetailHomeBinding by lazy {
ActivityDetailHomeBinding.inflate(layoutInflater)
}
+ private val adapter: GroupieAdapter by lazy {
+ GroupieAdapter()
+ }
+ private lateinit var searchResultIntent: SearchResultIntent
- private var extraId: String? = null
- private var extraSeatClass: String? = null
-
+ // Define global variables
+ private var returnId: String? = null
+ private var departureId: String? = null
+ private var seatClass: String? = null
+ private var adultCount: Int? = null
+ private var childCount: Int? = null
+ private var babyCount: Int? = null
+ private var roundTrip: Boolean? = null
private val detailViewModel: DetailHomeViewModel by viewModel()
+// private var extraRoundTrip: Boolean? = null
+// private var extraId: String? = null
+// private var extraSeatClass: String? = null
+// private var adultCount: Int? = 0
+// private var childCount: Int? = 0
+// private var babyCount: Int? = 0
+// private var returnFlightId: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- extraId = intent.getStringExtra("EXTRA_FLIGHT_ID")
- extraSeatClass = intent.getStringExtra("EXTRA_FLIGHT_SEATCLASS")
+ setHeaderTitle()
+ searchResultIntent = intent.getParcelableExtra("EXTRA_SEARCH_RESULT") ?: return
+ returnId = searchResultIntent.returnId
+ departureId = searchResultIntent.departureId
+ seatClass = searchResultIntent.seatClass
+ adultCount = searchResultIntent.adultCount
+ childCount = searchResultIntent.childCount
+ babyCount = searchResultIntent.babyCount
+ roundTrip = searchResultIntent.roundTrip
setOnClickListener()
observeData()
}
+ private fun setHeaderTitle() {
+ binding.headerDetailTicketHome.tvTitleHeader.text =
+ getString(R.string.text_flight_detail_header)
+ }
+
private fun observeData() {
- detailViewModel.getDetailTicketById(extraId!!, extraSeatClass!!).observe(this@DetailHomeActivity) { result ->
- result.proceedWhen(
- doOnSuccess = {
- it.payload?.let { flightDetail ->
- Log.d("detailTicket", "$flightDetail")
- setupBinding(flightDetail)
- }
- },
- doOnError = {
- Log.d("Error from Detail", "${it.message}")
- },
- )
+ if (roundTrip == true) {
+ getDepartureDetail()
+ getReturnDetail()
+ } else {
+ getDepartureDetail()
}
}
+ private fun getDepartureDetail() {
+ detailViewModel.getDetailTicketById(departureId!!, seatClass!!)
+ .observe(this@DetailHomeActivity) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ Handler(Looper.getMainLooper()).postDelayed({
+ val scrollView = findViewById(R.id.sv_detail_ticket)
+ val layoutDetailCard =
+ scrollView.findViewById(R.id.layout_detail_card)
+ binding.shimmerDetailTicket.isVisible = false
+ binding.svDetailTicket.isVisible = true
+ binding.btnSelectTicket.isEnabled = true
+ layoutDetailCard.isVisible = true
+ it.payload?.let { flightDetail ->
+ setupBinding(flightDetail)
+ binding.btnSelectTicket.setOnClickListener {
+ val intent =
+ Intent(this, BookersBiodataActivity::class.java).apply {
+ putExtra("EXTRAS_FLIGHT_DETAIL", flightDetail)
+ putExtra("EXTRA_ADULT_COUNT", adultCount)
+ putExtra("EXTRA_CHILD_COUNT", childCount)
+ putExtra("EXTRA_BABY_COUNT", babyCount)
+ }
+ startActivity(intent)
+ }
+ }
+ }, 1000)
+ Log.d("Succes from Detail", "halo")
+ },
+ doOnError = { error ->
+ binding.btnSelectTicket.isEnabled = false
+ binding.shimmerDetailTicket.isVisible = false
+ if (error.exception is NoInternetException) {
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvDetailTicket.setState(ContentState.ERROR_GENERAL)
+ }
+ Log.e("ChooseSeatActivity", "Error: ${error.exception?.message}")
+ },
+ doOnEmpty = {
+ binding.shimmerDetailTicket.isVisible = false
+ binding.svDetailTicket.isVisible = false
+ binding.btnSelectTicket.isVisible = false
+ },
+ )
+ }
+ }
+
+ private fun getReturnDetail() {
+ detailViewModel.getDetailTicketById(returnId!!, seatClass!!)
+ .observe(this@DetailHomeActivity) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ Handler(Looper.getMainLooper()).postDelayed({
+ val scrollView = findViewById(R.id.sv_detail_ticket)
+ val layoutDetailCard =
+ scrollView.findViewById(R.id.layout_detail_return_card)
+ binding.shimmerDetailTicket.isVisible = false
+ binding.svDetailTicket.isVisible = true
+ binding.btnSelectTicket.isEnabled = true
+ layoutDetailCard.isVisible = true
+
+ it.payload?.let { flightDetail ->
+ setupBindingReturnTicket(flightDetail)
+ binding.btnSelectTicket.setOnClickListener {
+ val intent =
+ Intent(this, BookersBiodataActivity::class.java).apply {
+ putExtra("EXTRAS_FLIGHT_DETAIL", flightDetail)
+ putExtra("EXTRA_ADULT_COUNT", adultCount)
+ putExtra("EXTRA_CHILD_COUNT", childCount)
+ putExtra("EXTRA_BABY_COUNT", babyCount)
+ }
+ startActivity(intent)
+ }
+ }
+ }, 1000)
+ Log.d("Succes from Detail", "halo")
+ },
+ doOnError = { error ->
+ binding.btnSelectTicket.isEnabled = false
+ binding.shimmerDetailTicket.isVisible = false
+ if (error.exception is NoInternetException) {
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvDetailTicket.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvDetailTicket.setState(ContentState.ERROR_GENERAL)
+ }
+ Log.e("ChooseSeatActivity", "Error: ${error.exception?.message}")
+ },
+ doOnEmpty = {
+ binding.shimmerDetailTicket.isVisible = false
+ binding.svDetailTicket.isVisible = false
+ binding.btnSelectTicket.isVisible = false
+ },
+ )
+ }
+ }
+
@SuppressLint("SetTextI18n")
private fun setupBinding(ticket: FlightDetailTicket) {
ticket.let {
- binding.layoutDetailCard.tvFlightDestinationDetailTicket.text = "${it.departureCity} - > ${it.arrivalCity} (${it.duration})"
+ binding.layoutDetailCard.ivAirlineDetailTicket.load(it.airplaneImg) {
+ crossfade(true)
+ error(R.mipmap.ic_launcher)
+ }
+ binding.layoutDetailCard.tvFlightDestinationDetailTicket.text =
+ "${it.departureCity} - > ${it.arrivalCity} (${it.duration})"
binding.layoutDetailCard.tvDepartureTimeDetailTicket.text = it.departureTime
binding.layoutDetailCard.tvDepartureDateDetailTicket.text = it.departureDate
- binding.layoutDetailCard.tvDepartureAirportDetailTicket.text = it.departureAirport
- binding.layoutDetailCard.tvTerminalDetailTicket.text = it.departureTerminal
+ binding.layoutDetailCard.tvDepartureAirportDetailTicket.text =
+ "${it.departureAirport} - ${it.departureTerminal}"
binding.layoutDetailCard.tvAirplaneNameDetailTicket.text = it.airplaneName
binding.layoutDetailCard.tvSeatClassDetailTicket.text = it.seatClass
binding.layoutDetailCard.tvAirplaneCodeDetailTicket.text = it.code
@@ -62,9 +232,58 @@ class DetailHomeActivity : AppCompatActivity() {
}
}
+ private fun setupBindingReturnTicket(ticket: FlightDetailTicket) {
+ ticket.let {
+ binding.layoutDetailReturnCard.ivAirlineDetailTicket.load(it.airplaneImg) {
+ crossfade(true)
+ error(R.mipmap.ic_launcher)
+ }
+ binding.layoutDetailReturnCard.tvFlightDestinationDetailTicket.text =
+ getString(
+ R.string.text_detail_return_flight_title,
+ it.departureCity,
+ it.arrivalCity,
+ it.duration,
+ )
+ binding.layoutDetailReturnCard.tvDepartureTimeDetailTicket.text = it.departureTime
+ binding.layoutDetailReturnCard.tvDepartureDateDetailTicket.text = it.departureDate
+ binding.layoutDetailReturnCard.tvDepartureAirportDetailTicket.text =
+ "${it.departureAirport} - ${it.departureTerminal}"
+ binding.layoutDetailReturnCard.tvAirplaneNameDetailTicket.text = it.airplaneName
+ binding.layoutDetailReturnCard.tvSeatClassDetailTicket.text = it.seatClass
+ binding.layoutDetailReturnCard.tvAirplaneCodeDetailTicket.text = it.code
+ binding.layoutDetailReturnCard.tvFacilitiesDetailTicket.text = it.facilities
+ binding.layoutDetailReturnCard.tvArrivalTimeDetailTicket.text = it.arrivalTime
+ binding.layoutDetailReturnCard.tvArrivalDateDetailTicket.text = it.arrivalDate
+ binding.layoutDetailReturnCard.tvDestinationAirportDetailTicket.text = it.arrivalAirport
+ }
+ }
+
private fun setOnClickListener() {
- binding.headerDetailTicketHome.ivBackBtnDetailTicketHome.setOnClickListener {
+ binding.headerDetailTicketHome.ivBack.setOnClickListener {
finish()
}
+ binding.btnSelectTicket.setOnClickListener {
+ detailViewModel.getDetailTicketById(returnId!!, seatClass!!)
+ .observe(this@DetailHomeActivity) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ it.payload?.let { flightDetail ->
+ val intent =
+ Intent(this, BookersBiodataActivity::class.java).apply {
+ putExtra("EXTRAS_FLIGHT_DETAIL", flightDetail)
+ putExtra("EXTRA_ADULT_COUNT", adultCount)
+ putExtra("EXTRA_CHILD_COUNT", childCount)
+ putExtra("EXTRA_BABY_COUNT", babyCount)
+ }
+ startActivity(intent)
+ }
+ },
+ doOnError = {
+ Log.d("Error from Detail", "${it.message}")
+ },
+ )
+ }
+ }
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeViewModel.kt
index a72347c..0cbb88c 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/DetailHomeViewModel.kt
@@ -9,7 +9,7 @@ class DetailHomeViewModel(
private val repo: FlightTicketRepository,
) : ViewModel() {
fun getDetailTicketById(
- id: String,
- seatClass: String,
- ) = repo.getDetailTicket(id = id, seatClass = seatClass).asLiveData(Dispatchers.IO)
+ id: String?,
+ seatClass: String?,
+ ) = repo.getDetailTicket(id = id.orEmpty(), seatClass = seatClass).asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/view_item/Items.kt b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/view_item/Items.kt
new file mode 100644
index 0000000..4b9047d
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/detail_home/view_item/Items.kt
@@ -0,0 +1,40 @@
+package com.kom.skyfly.presentation.home.detail_home.view_item
+
+import android.annotation.SuppressLint
+import android.view.View
+import com.kom.skyfly.R
+import com.kom.skyfly.data.model.home.flight.FlightTicket
+import com.kom.skyfly.databinding.LayoutItemCardTicketDetailHomeBinding
+import com.xwray.groupie.viewbinding.BindableItem
+
+class Items(
+ private val data: FlightTicket,
+ private val onItemClick: (item: FlightTicket) -> Unit,
+) : BindableItem() {
+ @SuppressLint("SetTextI18n")
+ override fun bind(
+ viewBinding: LayoutItemCardTicketDetailHomeBinding,
+ position: Int,
+ ) {
+ with(viewBinding) {
+ tvFlightDestinationDetailTicket.text =
+ "${data.departureCity} - > ${data.arrivalCity} (${data.duration})"
+ tvDepartureTimeDetailTicket.text = data.departureTime
+ tvDepartureDateDetailTicket.text = data.departureDate
+ tvDepartureAirportDetailTicket.text = "${data.departureAirport} - ${data.departureTerminal}"
+ tvAirplaneNameDetailTicket.text = data.airplaneName
+ tvSeatClassDetailTicket.text = data.seatClass
+ tvAirplaneCodeDetailTicket.text = data.code
+ tvFacilitiesDetailTicket.text = data.facilities
+ tvArrivalTimeDetailTicket.text = data.arrivalTime
+ tvArrivalDateDetailTicket.text = data.arrivalDate
+ tvDestinationAirportDetailTicket.text = data.arrivalAirport
+ }
+ }
+
+ override fun getLayout(): Int = R.layout.layout_item_card_ticket_detail_home
+
+ override fun initializeViewBinding(view: View): LayoutItemCardTicketDetailHomeBinding {
+ return LayoutItemCardTicketDetailHomeBinding.bind(view)
+ }
+}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchFragment.kt
index 196bdd3..ccd1ef8 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchFragment.kt
@@ -4,15 +4,21 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.Toast
+import androidx.core.view.isVisible
import androidx.lifecycle.map
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.kom.skyfly.R
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
import com.kom.skyfly.databinding.FragmentSearchBinding
+import com.kom.skyfly.presentation.home.search.adapter.SearchDestinationHistoryAdapter
+import com.kom.skyfly.presentation.home.search.adapter.SearchDestinationHistoryListener
import com.kom.skyfly.presentation.home.search.viewitems.Items
import com.kom.skyfly.presentation.main.MainViewModel
+import com.kom.skyfly.utils.afterTextChanged
import com.kom.skyfly.utils.proceedWhen
import com.xwray.groupie.GroupieAdapter
import org.koin.androidx.viewmodel.ext.android.activityViewModel
@@ -25,6 +31,19 @@ class SearchFragment : BottomSheetDialogFragment() {
private val adapter: GroupieAdapter by lazy {
GroupieAdapter()
}
+ private val searchHistoryAdapter: SearchDestinationHistoryAdapter by lazy {
+ SearchDestinationHistoryAdapter(
+ itemClick = { searchDestinationHistory ->
+ binding.layoutHomeSearchBar.etHomeSearch.setText(searchDestinationHistory.searchDestinationHistory)
+ },
+ searchDestinationHistoryListener =
+ object : SearchDestinationHistoryListener {
+ override fun onDeleteItemClicked(searchDestinationHistory: SearchDestinationHistory) {
+ searchViewModel.deleteSearchHistory(searchDestinationHistory)
+ }
+ },
+ )
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -45,18 +64,18 @@ class SearchFragment : BottomSheetDialogFragment() {
) {
super.onViewCreated(view, savedInstanceState)
setOnClickListener()
+ setList()
val maxPeekHeight =
resources.getDimensionPixelSize(
R.dimen.max_bottom_sheet_height,
)
setupBinding()
- getAirportData()
+ getRecentSearchAirport()
setBottomSheetMaxHeight(maxPeekHeight)
- // Extract names from the mockList
}
- private fun getAirportData() {
- searchViewModel.getAllAirports().observe(viewLifecycleOwner) { response ->
+ private fun getAirportData(city: String?) {
+ searchViewModel.getAllAirports(city = city).observe(viewLifecycleOwner) { response ->
response.proceedWhen(
doOnSuccess = { result ->
result.payload?.let { sectionedSearch ->
@@ -66,6 +85,7 @@ class SearchFragment : BottomSheetDialogFragment() {
sectionedSearch.map { data ->
Items(data) { item ->
mainViewModel.setDestination(item)
+ observeData(item.city)
dismiss()
}
}
@@ -77,6 +97,36 @@ class SearchFragment : BottomSheetDialogFragment() {
}
}
+ private fun observeData(searchHistory: String) {
+ searchViewModel.insertSearchHistory(searchHistory)
+ .observe(viewLifecycleOwner) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ Toast.makeText(requireContext(), "success", Toast.LENGTH_SHORT).show()
+ },
+ doOnError = {
+ Toast.makeText(requireContext(), "error", Toast.LENGTH_SHORT).show()
+ },
+ )
+ }
+ }
+
+ private fun getRecentSearchAirport() {
+ searchViewModel.getAllSearchHistory().observe(viewLifecycleOwner) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ binding.tvHomeDeleteRecentSearches.isVisible = true
+ binding.tvTitleHomeRecentSearches.isVisible = true
+ binding.rvRecentDestinationSearch.isVisible = true
+ binding.rvHomeSearchPage.isVisible = false
+ result.payload?.let {
+ searchHistoryAdapter.submitData(it)
+ }
+ },
+ )
+ }
+ }
+
private fun setupBinding() {
binding.rvHomeSearchPage.apply {
layoutManager = LinearLayoutManager(requireContext())
@@ -84,10 +134,16 @@ class SearchFragment : BottomSheetDialogFragment() {
}
}
+ private fun setList() {
+ binding.rvRecentDestinationSearch.layoutManager = LinearLayoutManager(requireContext())
+ binding.rvRecentDestinationSearch.adapter = searchHistoryAdapter
+ }
+
private fun setBottomSheetMaxHeight(maxPeekHeight: Int) {
dialog?.setOnShowListener {
val bottomSheetDialog = it as? BottomSheetDialog
- val bottomSheet = bottomSheetDialog?.findViewById(com.google.android.material.R.id.design_bottom_sheet)
+ val bottomSheet =
+ bottomSheetDialog?.findViewById(com.google.android.material.R.id.design_bottom_sheet)
bottomSheet?.let { sheet ->
val behavior = BottomSheetBehavior.from(sheet)
behavior.peekHeight = maxPeekHeight
@@ -100,5 +156,18 @@ class SearchFragment : BottomSheetDialogFragment() {
binding.layoutHomeSearchBar.ivCloseSearch.setOnClickListener {
dismiss()
}
+ binding.layoutHomeSearchBar.etHomeSearch.afterTextChanged {
+ if (it.isNotEmpty()) {
+ binding.rvHomeSearchPage.isVisible = true
+ binding.rvRecentDestinationSearch.isVisible = false
+ binding.tvHomeDeleteRecentSearches.isVisible = false
+ getAirportData(it)
+ } else {
+ binding.tvTitleHomeRecentSearches.isVisible = true
+ binding.rvHomeSearchPage.isVisible = false
+ binding.rvRecentDestinationSearch.isVisible = true
+ getRecentSearchAirport()
+ }
+ }
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchViewModel.kt
index 8ff6c4b..c361e3a 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search/SearchViewModel.kt
@@ -1,12 +1,37 @@
package com.kom.skyfly.presentation.home.search
+import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
import com.kom.skyfly.data.repository.home.airport.AirportRepository
+import com.kom.skyfly.utils.ResultWrapper
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
class SearchViewModel(
private val repo: AirportRepository,
) : ViewModel() {
- fun getAllAirports() = repo.getAllAirportData().asLiveData(Dispatchers.IO)
+ fun getAllAirports(city: String? = null) = repo.getAllAirportData(city = city).asLiveData(Dispatchers.IO)
+
+ fun getAllSearchHistory() = repo.getUserSearchDestinationHistory().asLiveData(Dispatchers.IO)
+
+ fun deleteSearchHistory(item: SearchDestinationHistory) {
+ viewModelScope.launch(Dispatchers.IO) {
+ repo.deleteSearchDestinationHistory(item).collect()
+ }
+ }
+
+ fun deleteAllSearchHistory() {
+ viewModelScope.launch {
+ repo.deleteAllSearchDestinationHistory().collect()
+ }
+ }
+
+ fun insertSearchHistory(searchDestinationHistory: String): LiveData> {
+ return repo.createSearchDestinationHistory(searchDestinationHistory)
+ .asLiveData(Dispatchers.IO)
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search/adapter/SearchDestinationHistoryAdapter.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search/adapter/SearchDestinationHistoryAdapter.kt
new file mode 100644
index 0000000..92975eb
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search/adapter/SearchDestinationHistoryAdapter.kt
@@ -0,0 +1,80 @@
+package com.kom.skyfly.presentation.home.search.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.AsyncListDiffer
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.kom.skyfly.data.model.home.search_history.SearchDestinationHistory
+import com.kom.skyfly.databinding.LayoutItemAirportRecentSearchBinding
+
+class SearchDestinationHistoryAdapter(
+ private val itemClick: (
+ SearchDestinationHistory,
+ ) -> Unit,
+ private val searchDestinationHistoryListener: SearchDestinationHistoryListener? = null,
+) : RecyclerView.Adapter() {
+ class SearchDestinationHistoryViewHolder(
+ private val binding: LayoutItemAirportRecentSearchBinding,
+ val itemClick: (SearchDestinationHistory) -> Unit,
+ private val searchDestinationHistoryListener: SearchDestinationHistoryListener?,
+ ) : RecyclerView.ViewHolder(binding.root) {
+ fun bindView(item: SearchDestinationHistory) {
+ binding.tvFlightHomeRecentSearch.text = item.searchDestinationHistory
+ itemView.setOnClickListener { itemClick(item) }
+ setClickListeners(item)
+ }
+
+ private fun setClickListeners(item: SearchDestinationHistory) {
+ with(binding) {
+ ivDeleteItem.setOnClickListener { searchDestinationHistoryListener?.onDeleteItemClicked(item) }
+ }
+ }
+ }
+
+ private val dataDiffer =
+ AsyncListDiffer(
+ this,
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: SearchDestinationHistory,
+ newItem: SearchDestinationHistory,
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(
+ oldItem: SearchDestinationHistory,
+ newItem: SearchDestinationHistory,
+ ): Boolean {
+ return oldItem.hashCode() == newItem.hashCode()
+ }
+ },
+ )
+
+ fun submitData(data: List) {
+ dataDiffer.submitList(data)
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int,
+ ): SearchDestinationHistoryViewHolder {
+ val binding =
+ LayoutItemAirportRecentSearchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return SearchDestinationHistoryViewHolder(binding, itemClick, searchDestinationHistoryListener)
+ }
+
+ override fun onBindViewHolder(
+ holder: SearchDestinationHistoryViewHolder,
+ position: Int,
+ ) {
+ (holder.bindView(dataDiffer.currentList[position]))
+ }
+
+ override fun getItemCount(): Int = dataDiffer.currentList.size
+}
+
+interface SearchDestinationHistoryListener {
+ fun onDeleteItemClicked(searchDestinationHistory: SearchDestinationHistory)
+}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search/viewitems/Items.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search/viewitems/Items.kt
index 5029fe3..6d93b9a 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search/viewitems/Items.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search/viewitems/Items.kt
@@ -24,7 +24,6 @@ class Items(
}
}
-
override fun getLayout(): Int = R.layout.layout_item_airport_search
override fun initializeViewBinding(view: View): LayoutItemAirportSearchBinding {
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultActivity.kt
index 017c83a..6182b38 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultActivity.kt
@@ -2,15 +2,23 @@ package com.kom.skyfly.presentation.home.search_result
import android.content.Intent
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
import android.util.Log
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
+import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.data.model.home.Calendar
+import com.kom.skyfly.data.model.home.intent.SearchResultIntent
import com.kom.skyfly.databinding.ActivitySearchResultBinding
+import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.presentation.home.detail_home.DetailHomeActivity
import com.kom.skyfly.presentation.home.search_result.view_items.CalendarAdapter
import com.kom.skyfly.presentation.home.search_result.view_items.Items
+import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section
@@ -20,7 +28,7 @@ import java.time.format.DateTimeFormatter
import java.time.format.TextStyle
import java.util.Locale
-class SearchResultActivity : AppCompatActivity() {
+class SearchResultActivity : BaseActivity() {
private val binding: ActivitySearchResultBinding by lazy {
ActivitySearchResultBinding.inflate(layoutInflater)
}
@@ -29,8 +37,8 @@ class SearchResultActivity : AppCompatActivity() {
}
private val calendarAdapter: CalendarAdapter by lazy {
CalendarAdapter {
+ calendarAdapter.setSelectedDate(it.date)
getAllTicketData(it.date)
- Toast.makeText(this, "${it.date}", Toast.LENGTH_SHORT).show()
}
}
@@ -38,14 +46,28 @@ class SearchResultActivity : AppCompatActivity() {
private var departureAirport: String? = null
private var arrivalAirport: String? = null
private var departureTime: String? = null
+ private var adultCount: Int? = null
+ private var childrenCount: Int? = null
+ private var babyCount: Int? = null
+ private var totalPassenger: Int? = null
+ private var seatClass: String? = null
+ private var extraRoundTrip: Boolean? = null
+ private var returnDate: String? = null
+ private var flightId: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
-
+ seatClass = intent.getStringExtra("EXTRA_SEAT_CLASS")
departureAirport = intent.getStringExtra("EXTRA_DEPARTURE_AIRPORT")
arrivalAirport = intent.getStringExtra("EXTRA_ARRIVAL_AIRPORT")
departureTime = intent.getStringExtra("EXTRA_DEPARTURE_TIME")
+ returnDate = intent.getStringExtra("EXTRA_RETURN_TIME")
+ adultCount = intent.getIntExtra("EXTRA_ADULT_COUNT", 0)
+ totalPassenger = intent.getIntExtra("EXTRA_TOTAL_PASSENGER", 0)
+ childrenCount = intent.getIntExtra("EXTRA_CHILD_COUNT", 0)
+ babyCount = intent.getIntExtra("EXTRA_BABY_COUNT", 0)
+ extraRoundTrip = intent.getBooleanExtra("EXTRA_ROUND_TRIP", false)
setOnClickListener()
setupBinding()
getCalendarData()
@@ -54,7 +76,8 @@ class SearchResultActivity : AppCompatActivity() {
private fun getCalendarData() {
val recyclerView = binding.rvDateSearchResult
- recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
+ recyclerView.layoutManager =
+ LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
val dateTime = YearMonth.parse(departureTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
@@ -71,13 +94,17 @@ class SearchResultActivity : AppCompatActivity() {
calendarAdapter.submitData(days)
}
- private fun getAllTicketData(date: String) {
- searchResultViewModel.getAllFlightTicket(
+ private fun getAllReturnTicketData(date: String) {
+ searchResultViewModel.getReturnFlightTicket(
page = 1,
arrivalAirport = arrivalAirport!!,
departureAirport = departureAirport!!,
departureDate = date,
- seatClass = "ECONOMY",
+ seatClass = seatClass,
+ adult = adultCount,
+ children = childrenCount,
+ baby = babyCount,
+ returnDate = returnDate,
).observe(this) { response ->
response.proceedWhen(
doOnSuccess = {
@@ -89,16 +116,23 @@ class SearchResultActivity : AppCompatActivity() {
val data =
sectionedSearch.map { data ->
Items(data) { item ->
+ Log.d("dari return ticket data", "$item")
+ val searchResultIntent =
+ SearchResultIntent(
+ returnId = item?.id,
+ departureId = flightId,
+ seatClass = item?.seatClass,
+ adultCount = adultCount,
+ childCount = childrenCount,
+ babyCount = babyCount,
+ roundTrip = extraRoundTrip,
+ )
val intent =
Intent(
this@SearchResultActivity,
DetailHomeActivity::class.java,
).apply {
- putExtra("EXTRA_FLIGHT_ID", item?.id)
- putExtra(
- "EXTRA_FLIGHT_SEATCLASS",
- item?.seatClass,
- )
+ putExtra("EXTRA_SEARCH_RESULT", searchResultIntent)
}
startActivity(intent)
}
@@ -114,7 +148,7 @@ class SearchResultActivity : AppCompatActivity() {
adapter.clear()
},
doOnError = {
- Log.e("SearchResultActivity", "Error fetching data: ${it.exception?.message}")
+ Log.e("SearchResultActivityReturn", "Error fetching data: ${it.exception?.message}")
},
doOnLoading = {
Log.d("SearchResultActivity", "Loading data")
@@ -123,7 +157,117 @@ class SearchResultActivity : AppCompatActivity() {
}
}
+ private fun getAllTicketData(date: String) {
+ searchResultViewModel.getAllFlightTicket(
+ page = 1,
+ arrivalAirport = arrivalAirport!!,
+ departureAirport = departureAirport!!,
+ departureDate = date,
+ seatClass = seatClass,
+ adult = adultCount,
+ children = childrenCount,
+ baby = babyCount,
+ returnDate = returnDate,
+ ).observe(this) { response ->
+ response.proceedWhen(
+ doOnSuccess = {
+ Handler(Looper.getMainLooper()).postDelayed({
+ binding.shimmerTicket.isVisible = false
+ binding.rvDateSearchResult.isVisible = true
+ binding.rvItemTicketsSearchResult.isVisible = true
+ it.payload?.let { sectionedSearch ->
+ val sections =
+ sectionedSearch.map {
+ Section().apply {
+ val data =
+ sectionedSearch.map { data ->
+ Items(data) { item ->
+ if (extraRoundTrip == true) {
+ flightId = item?.id
+ getAllReturnTicketData(departureTime!!)
+ } else {
+ val searchResultIntent =
+ SearchResultIntent(
+ departureId = item?.id,
+ seatClass = item?.seatClass,
+ adultCount = adultCount,
+ childCount = childrenCount,
+ babyCount = babyCount,
+ roundTrip = extraRoundTrip,
+ returnId = "",
+ )
+ val intent =
+ Intent(
+ this@SearchResultActivity,
+ DetailHomeActivity::class.java,
+ ).apply {
+ putExtra("EXTRA_SEARCH_RESULT", searchResultIntent)
+ }
+ startActivity(intent)
+ }
+ }
+ }
+ addAll(data)
+ }
+ }
+ adapter.clear()
+ adapter.update(sections)
+ }
+ }, 1000)
+ Log.d("SearchResultActivity", "Data fetched successfully")
+ },
+ doOnEmpty = {
+ adapter.clear()
+ binding.shimmerTicket.isVisible = false
+ binding.csvTicketResult.setState(
+ ContentState.EMPTY,
+ getString(R.string.text_no_tickets_for_selected_date),
+ )
+ },
+ doOnError = { error ->
+ binding.shimmerTicket.isVisible = false
+ if (error.exception is NoInternetException) {
+ binding.csvTicketResult.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ errorHandler(error.exception)
+ binding.csvTicketResult.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ errorHandler(error.exception)
+ binding.csvTicketResult.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
+ } else {
+ binding.csvTicketResult.setState(ContentState.ERROR_GENERAL)
+ }
+ Log.e("SearchResultActivity", "Error: ${error.exception?.message}")
+ },
+ doOnLoading = {
+ binding.shimmerTicket.isVisible = true
+ binding.csvTicketResult.isVisible = false
+ binding.rvItemTicketsSearchResult.isVisible = false
+ binding.rvDateSearchResult.isVisible = true
+ },
+ )
+ }
+ }
+
private fun setupBinding() {
+ binding.headerFlightSearchResult.tvTitleHeader.text =
+ getString(
+ R.string.text_header_search_result,
+ departureAirport,
+ arrivalAirport,
+ totalPassenger.toString(),
+ seatClass,
+ )
binding.rvItemTicketsSearchResult.apply {
layoutManager = LinearLayoutManager(this@SearchResultActivity)
adapter = this@SearchResultActivity.adapter
@@ -131,7 +275,7 @@ class SearchResultActivity : AppCompatActivity() {
}
private fun setOnClickListener() {
- binding.headerFlightSearchResult.ivBackBtnSearchResult.setOnClickListener {
+ binding.headerFlightSearchResult.ivBack.setOnClickListener {
finish()
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultViewModel.kt
index ea01519..7b0a46a 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/SearchResultViewModel.kt
@@ -15,7 +15,13 @@ class SearchResultViewModel(
departureAirport: String,
arrivalAirport: String,
departureDate: String,
- seatClass: String,
+ returnDate: String? = null,
+ seatClass: String?,
+ limit: Int? = 20,
+ arrivalDate: String? = null,
+ adult: Int? = 1,
+ children: Int? = 0,
+ baby: Int? = 0,
) = flightRepository.getAllTicket(
search = search,
page = page,
@@ -23,5 +29,39 @@ class SearchResultViewModel(
arrivalAirport = arrivalAirport,
departureDate = departureDate,
seatClass = seatClass,
+ limit = limit,
+ arrivalDate = arrivalDate,
+ adult = adult,
+ children = children,
+ baby = baby,
+ returnDate = returnDate,
+ ).asLiveData(Dispatchers.IO)
+
+ fun getReturnFlightTicket(
+ search: String? = null,
+ page: Int,
+ departureAirport: String,
+ arrivalAirport: String,
+ departureDate: String,
+ returnDate: String? = null,
+ seatClass: String?,
+ limit: Int? = 20,
+ arrivalDate: String? = null,
+ adult: Int? = 1,
+ children: Int? = 0,
+ baby: Int? = 0,
+ ) = flightRepository.getReturnTicket(
+ search = search,
+ page = page,
+ departureAirport = departureAirport,
+ arrivalAirport = arrivalAirport,
+ departureDate = departureDate,
+ seatClass = seatClass,
+ limit = limit,
+ arrivalDate = arrivalDate,
+ adult = adult,
+ children = children,
+ baby = baby,
+ returnDate = returnDate,
).asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/CalendarAdapter.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/CalendarAdapter.kt
index 3ab09e9..f2f8d2a 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/CalendarAdapter.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/CalendarAdapter.kt
@@ -1,25 +1,48 @@
package com.kom.skyfly.presentation.home.search_result.view_items
import android.annotation.SuppressLint
+import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
+import com.kom.skyfly.R
import com.kom.skyfly.data.model.home.Calendar
import com.kom.skyfly.databinding.LayoutDateBinding
class CalendarAdapter(private val itemClick: (Calendar) -> Unit) :
RecyclerView.Adapter() {
+ private var selectedDate: String? = null
+
class ItemCalendarViewHolder(
private val binding: LayoutDateBinding,
val itemClick: (Calendar) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("ResourceAsColor")
- fun bindView(item: Calendar) {
+ fun bindView(
+ item: Calendar,
+ isSelected: Boolean,
+ ) {
with(item) {
binding.tvCalendarDay.text = day
binding.tvCalendarDate.text = date
+ val context = binding.root.context
+ if (isSelected) {
+ binding.cvCalendar.setCardBackgroundColor(
+ ContextCompat.getColor(
+ context,
+ R.color.md_theme_primaryFixed_mediumContrast,
+ ),
+ )
+ binding.tvCalendarDay.setTextColor(Color.WHITE)
+ binding.tvCalendarDate.setTextColor(Color.WHITE)
+ } else {
+ binding.cvCalendar.setCardBackgroundColor(Color.WHITE)
+ binding.tvCalendarDay.setTextColor(Color.BLACK)
+ binding.tvCalendarDate.setTextColor(Color.BLACK)
+ }
itemView.setOnClickListener {
itemClick(this)
@@ -66,6 +89,13 @@ class CalendarAdapter(private val itemClick: (Calendar) -> Unit) :
holder: ItemCalendarViewHolder,
position: Int,
) {
- holder.bindView(asyncDataDiffer.currentList[position])
+ val item = asyncDataDiffer.currentList[position]
+ val isSelected = item.date == selectedDate
+ holder.bindView(item, isSelected)
+ }
+
+ fun setSelectedDate(date: String) {
+ selectedDate = date
+ notifyDataSetChanged()
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/Items.kt b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/Items.kt
index 90ca510..7b7181f 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/Items.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/search_result/view_items/Items.kt
@@ -2,9 +2,12 @@ package com.kom.skyfly.presentation.home.search_result.view_items
import android.annotation.SuppressLint
import android.view.View
+import coil.load
import com.kom.skyfly.R
import com.kom.skyfly.data.model.home.flight.FlightTicket
+import com.kom.skyfly.data.model.home.flight_detail.FlightDetailTicket
import com.kom.skyfly.databinding.LayoutItemCardSearchResultBinding
+import com.kom.skyfly.utils.formatToRupiah
import com.xwray.groupie.viewbinding.BindableItem
class Items(
@@ -17,13 +20,60 @@ class Items(
position: Int,
) {
with(viewBinding) {
+ ivIcAirlineSearchResult.load(data?.airplaneImg) {
+ crossfade(true)
+ error(R.drawable.img_airline)
+ }
+ tvTimeDepartureSearchResult.text = data?.departureTime
+ tvTimeArrivalSearchResult.text = data?.arrivalTime
+ tvTargetDepartureSearchResult.text = data?.departureCountryCode
+ tvTargetArrivalSearchResult.text = data?.arrivalCountryCode
+ tvFlightDurationSearchResult.text = data?.duration
+ if (data?.directNotes == false) {
+ tvTransitOptionSearchResult.text = "Direct"
+ } else {
+ tvTransitOptionSearchResult.text = "Transit"
+ }
+ tvPriceSearchResult.text = data?.price.formatToRupiah().toString()
+ tvAirlineSearchResult.text = "${data?.airplaneName} - ${data?.seatClass}"
+ viewBinding.root.setOnClickListener {
+ onItemClick(data)
+ }
+ }
+ }
+
+ override fun getLayout(): Int = R.layout.layout_item_card_search_result
+
+ override fun initializeViewBinding(view: View): LayoutItemCardSearchResultBinding {
+ return LayoutItemCardSearchResultBinding.bind(view)
+ }
+}
+
+class FlightDetailItem(
+ private val data: FlightDetailTicket?,
+ private val onItemClick: (item: FlightDetailTicket?) -> Unit,
+) : BindableItem() {
+ @SuppressLint("SetTextI18n")
+ override fun bind(
+ viewBinding: LayoutItemCardSearchResultBinding,
+ position: Int,
+ ) {
+ with(viewBinding) {
+ ivIcAirlineSearchResult.load(data?.airplaneImg) {
+ crossfade(true)
+ error(R.mipmap.ic_launcher)
+ }
tvTimeDepartureSearchResult.text = data?.departureTime
tvTimeArrivalSearchResult.text = data?.arrivalTime
tvTargetDepartureSearchResult.text = data?.departureCountryCode
tvTargetArrivalSearchResult.text = data?.arrivalCountryCode
tvFlightDurationSearchResult.text = data?.duration
- tvTransitOptionSearchResult.text = "Direct"
- tvPriceSearchResult.text = data?.price.toString()
+ if (data?.transitNotes == false) {
+ tvTransitOptionSearchResult.text = "Direct"
+ } else {
+ tvTransitOptionSearchResult.text = "Transit"
+ }
+ tvPriceSearchResult.text = data?.price.formatToRupiah().toString()
tvAirlineSearchResult.text = "${data?.airplaneName} - ${data?.seatClass}"
viewBinding.root.setOnClickListener {
onItemClick(data)
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassFragment.kt
index f140791..a9446fa 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassFragment.kt
@@ -4,12 +4,27 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.kom.skyfly.R
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
import com.kom.skyfly.databinding.FragmentSeatClassBinding
+import com.kom.skyfly.presentation.home.seatclass.adapter.SeatClassAdapter
+import com.kom.skyfly.presentation.main.MainViewModel
+import org.koin.androidx.viewmodel.ext.android.activityViewModel
+import org.koin.androidx.viewmodel.ext.android.viewModel
class SeatClassFragment : BottomSheetDialogFragment() {
private lateinit var binding: FragmentSeatClassBinding
+ private val seatClassViewModel: SeatClassViewModel by viewModel()
+ private val mainViewModel: MainViewModel by activityViewModel()
+ private var seatClassName: String? = null
+ private val adapter: SeatClassAdapter by lazy {
+ SeatClassAdapter { item ->
+ item.let {
+ seatClassName = it.seatClassName
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -19,8 +34,36 @@ class SeatClassFragment : BottomSheetDialogFragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
- ): View? {
- // Inflate the layout for this fragment
- return inflater.inflate(R.layout.fragment_seat_class, container, false)
+ ): View {
+ binding = FragmentSeatClassBinding.inflate(layoutInflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(
+ view: View,
+ savedInstanceState: Bundle?,
+ ) {
+ super.onViewCreated(view, savedInstanceState)
+ setupBinding(seatClassViewModel.getSeatClass())
+ setOnClickListener()
+ }
+
+ private fun setupBinding(data: List) {
+ binding.rvSeatclassCategories.apply {
+ layoutManager =
+ LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
+ adapter = this@SeatClassFragment.adapter
+ }
+ adapter.submitData(data)
+ }
+
+ private fun setOnClickListener() {
+ binding.ivHomeCloseSeatclass.setOnClickListener {
+ dismiss()
+ }
+ binding.btnSeatclassSave.setOnClickListener {
+ mainViewModel.setSeatClass(seatClassName)
+ dismiss()
+ }
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassViewModel.kt
new file mode 100644
index 0000000..816f5f7
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/SeatClassViewModel.kt
@@ -0,0 +1,8 @@
+package com.kom.skyfly.presentation.home.seatclass
+
+import androidx.lifecycle.ViewModel
+import com.kom.skyfly.data.repository.home.flight_ticket.FlightTicketRepository
+
+class SeatClassViewModel(private val repo: FlightTicketRepository) : ViewModel() {
+ fun getSeatClass() = repo.getSeatClassTicket()
+}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/adapter/SeatClassAdapter.kt b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/adapter/SeatClassAdapter.kt
new file mode 100644
index 0000000..875537e
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/presentation/home/seatclass/adapter/SeatClassAdapter.kt
@@ -0,0 +1,114 @@
+package com.kom.skyfly.presentation.home.seatclass.adapter
+
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.AsyncListDiffer
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.kom.skyfly.R
+import com.kom.skyfly.data.model.home.seat_class.SeatClassHome
+import com.kom.skyfly.databinding.LayoutItemSeatclassCardBinding
+
+class SeatClassAdapter(private val itemClick: (SeatClassHome) -> Unit) :
+ RecyclerView.Adapter() {
+ private var selectedItemPosition = RecyclerView.NO_POSITION
+
+ private val dataDiffer =
+ AsyncListDiffer(
+ this,
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: SeatClassHome,
+ newItem: SeatClassHome,
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(
+ oldItem: SeatClassHome,
+ newItem: SeatClassHome,
+ ): Boolean {
+ return oldItem == newItem
+ }
+ },
+ )
+
+ fun submitData(data: List) {
+ dataDiffer.submitList(data)
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int,
+ ): ItemSeatClassViewHolder {
+ val binding =
+ LayoutItemSeatclassCardBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false,
+ )
+ return ItemSeatClassViewHolder(binding, itemClick)
+ }
+
+ override fun onBindViewHolder(
+ holder: ItemSeatClassViewHolder,
+ position: Int,
+ ) {
+ holder.bindView(dataDiffer.currentList[position], position == selectedItemPosition)
+ }
+
+ override fun getItemCount(): Int = dataDiffer.currentList.size
+
+ inner class ItemSeatClassViewHolder(
+ private val binding: LayoutItemSeatclassCardBinding,
+ private val itemClick: (SeatClassHome) -> Unit,
+ ) : RecyclerView.ViewHolder(binding.root) {
+ fun bindView(
+ item: SeatClassHome,
+ isSelected: Boolean,
+ ) {
+ with(item) {
+ binding.tvSeatclassTitle.text = seatClassName
+ val bgColor =
+ if (isSelected) {
+ ContextCompat.getColor(
+ binding.root.context,
+ R.color.md_theme_primaryFixed_mediumContrast,
+ )
+ } else {
+ ContextCompat.getColor(binding.root.context, android.R.color.white)
+ }
+ binding.cvSeatClass.setCardBackgroundColor(bgColor)
+
+ val titleTextColor =
+ if (isSelected) {
+ Color.WHITE
+ } else {
+ ContextCompat.getColor(binding.root.context, android.R.color.black)
+ }
+ if (isSelected) {
+ binding.ivChecklist.isVisible = true
+ } else {
+ binding.ivChecklist.isVisible = false
+ }
+ binding.tvSeatclassTitle.setTextColor(titleTextColor)
+ itemView.setOnClickListener {
+ itemClick(item)
+ setSelected(adapterPosition)
+ }
+ }
+ }
+
+ private fun setSelected(position: Int) {
+ if (selectedItemPosition != position) {
+ val previousSelected = selectedItemPosition
+ selectedItemPosition = position
+ notifyItemChanged(previousSelected)
+ notifyItemChanged(selectedItemPosition)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/login/LoginActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/login/LoginActivity.kt
index ef97db1..1d41b7d 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/login/LoginActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/login/LoginActivity.kt
@@ -73,7 +73,7 @@ class LoginActivity : AppCompatActivity() {
result.proceedWhen(
doOnSuccess = {
binding.pbLoading.isVisible = false
- binding.btnLogin.isVisible = true
+ binding.btnLogin.isEnabled = true
it.payload?.let { payload ->
val token = payload.token
loginViewModel.saveUserToken(token)
@@ -84,7 +84,10 @@ class LoginActivity : AppCompatActivity() {
doOnError = {
binding.pbLoading.isVisible = false
if (it.exception is NoInternetException) {
- binding.csvLogin.setState(ContentState.ERROR_NETWORK_GENERAL, "Tidak ada internet!")
+ binding.csvLogin.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ "Tidak ada internet!",
+ )
} else {
Toasty.error(
this@LoginActivity,
@@ -93,11 +96,11 @@ class LoginActivity : AppCompatActivity() {
true,
).show()
}
- binding.btnLogin.isVisible = true
+ binding.btnLogin.isEnabled = true
},
doOnLoading = {
binding.pbLoading.isVisible = true
- binding.btnLogin.isVisible = false
+ binding.btnLogin.isEnabled = false
},
)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/main/MainActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/main/MainActivity.kt
index 06a0410..a10ffbd 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/main/MainActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/main/MainActivity.kt
@@ -8,7 +8,7 @@ import androidx.navigation.ui.setupWithNavController
import com.kom.skyfly.R
import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.databinding.ActivityMainBinding
-import com.kom.skyfly.presentation.bottomsheetsdialog.BottomSheetsDialogFragment
+import com.kom.skyfly.presentation.bottomsheetsdialog.NotLoginBottomSheets
import com.kom.skyfly.presentation.checkout.bookersbiodata.BookersBiodataActivity
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -32,7 +32,7 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setBottomNavbar()
- navigate()
+// navigate()
}
private fun setBottomNavbar() {
@@ -52,7 +52,7 @@ class MainActivity : BaseActivity() {
private fun openNotLoggedInModal() {
if (!supportFragmentManager.isStateSaved) {
- val bottomSheetFragment = BottomSheetsDialogFragment()
+ val bottomSheetFragment = NotLoginBottomSheets()
bottomSheetFragment.show(supportFragmentManager, bottomSheetFragment.tag)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/main/MainViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/main/MainViewModel.kt
index fe670e5..3e9c9ce 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/main/MainViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/main/MainViewModel.kt
@@ -101,4 +101,20 @@ class MainViewModel(
passengerBabyCountLiveData.postValue(count)
}
}
+
+ // Set seatClassData
+ private val _seatClass = MutableLiveData()
+ val seatClass: LiveData get() = _seatClass
+
+ fun setSeatClass(value: String?) {
+ _seatClass.value = value
+ }
+
+ // Set roundTrip
+ private val _roundTrip = MutableLiveData(false)
+ val roundTrip: LiveData get() = _roundTrip
+
+ fun setRoundTrip(value: Boolean?) {
+ _roundTrip.value = value
+ }
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/notification/NotificationFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/notification/NotificationFragment.kt
index 3fe2287..b7f1cdf 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/notification/NotificationFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/notification/NotificationFragment.kt
@@ -2,17 +2,24 @@ package com.kom.skyfly.presentation.notification
import android.content.Intent
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
+import com.kom.skyfly.R
+import com.kom.skyfly.core.BaseActivity
import com.kom.skyfly.data.model.notification.Notification
import com.kom.skyfly.databinding.FragmentNotificationBinding
import com.kom.skyfly.presentation.common.views.ContentState
import com.kom.skyfly.presentation.notification.adapter.NotificationAdapter
import com.kom.skyfly.presentation.notificationdetail.NotificationDetailActivity
import com.kom.skyfly.utils.NoInternetException
+import com.kom.skyfly.utils.ServerErrorException
+import com.kom.skyfly.utils.UnAuthorizeException
import com.kom.skyfly.utils.proceedWhen
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -57,11 +64,13 @@ class NotificationFragment : Fragment() {
notificationViewModel.getAllNotification().observe(viewLifecycleOwner) { result ->
result.proceedWhen(
doOnSuccess = {
- binding.srlNotification.isRefreshing = false
- binding.shmProgressNotification.isVisible = false
- binding.csvNotification.setState(ContentState.SUCCESS)
- binding.rvNotification.isVisible = true
- it.payload?.let { data -> bindNotificationList(data) }
+ Handler(Looper.getMainLooper()).postDelayed({
+ binding.srlNotification.isRefreshing = false
+ binding.shmProgressNotification.isVisible = false
+ binding.csvNotification.setState(ContentState.SUCCESS)
+ binding.rvNotification.isVisible = true
+ it.payload?.let { data -> bindNotificationList(data) }
+ }, 1000)
},
doOnLoading = {
binding.srlNotification.isRefreshing = false
@@ -73,14 +82,32 @@ class NotificationFragment : Fragment() {
binding.csvNotification.setState(ContentState.EMPTY)
binding.shmProgressNotification.isVisible = false
},
- doOnError = {
+ doOnError = { error ->
binding.srlNotification.isRefreshing = false
- if (it.exception is NoInternetException) {
- binding.csvNotification.setState(ContentState.ERROR_NETWORK)
+ binding.shmProgressNotification.isVisible = false
+
+ if (error.exception is NoInternetException) {
+ binding.csvNotification.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.no_internet_connection),
+ )
+ } else if (error.exception is UnAuthorizeException) {
+ (activity as BaseActivity).errorHandler(error.exception)
+ binding.csvNotification.setState(
+ ContentState.ERROR_NETWORK_GENERAL,
+ getString(R.string.text_session_expired_please_login_again),
+ )
+ } else if (error.exception is ServerErrorException) {
+ (activity as BaseActivity).errorHandler(error.exception)
+ binding.csvNotification.setState(
+ ContentState.ERROR_NETWORK,
+ getString(R.string.text_server_error_please_try_again_later),
+ R.drawable.img_empty_data,
+ )
} else {
binding.csvNotification.setState(ContentState.ERROR_GENERAL)
}
- binding.shmProgressNotification.isVisible = false
+ Log.e("NotificationFragment", "Error: ${error.exception?.message}")
},
)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/notificationdetail/NotificationDetailActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/notificationdetail/NotificationDetailActivity.kt
index 4764710..f5d3849 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/notificationdetail/NotificationDetailActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/notificationdetail/NotificationDetailActivity.kt
@@ -34,9 +34,6 @@ class NotificationDetailActivity : AppCompatActivity() {
binding.ivBack.setOnClickListener {
onBackPressed()
}
- binding.ivTrash.setOnClickListener {
- notification?.let { deleteNotification(it) }
- }
}
private fun displayNotificationDetails(notification: Notification) {
@@ -49,6 +46,9 @@ class NotificationDetailActivity : AppCompatActivity() {
"Promotion" -> {
binding.ivNotificationType.setImageResource(R.drawable.ic_promotion)
}
+ "Promotions" -> {
+ binding.ivNotificationType.setImageResource(R.drawable.ic_promotion)
+ }
"Warning" -> {
binding.ivNotificationType.setImageResource(R.drawable.ic_warning)
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/register/RegisterActivity.kt b/app/src/main/java/com/kom/skyfly/presentation/register/RegisterActivity.kt
index 05452c7..a9e6297 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/register/RegisterActivity.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/register/RegisterActivity.kt
@@ -50,8 +50,9 @@ class RegisterActivity : AppCompatActivity() {
private fun openOtpModal(
token: String?,
email: String?,
+ phoneNumber: String?,
) {
- val verifyOtpFragment = VerifyOtpFragment.newInstance(token, email)
+ val verifyOtpFragment = VerifyOtpFragment.newInstance(token, email, phoneNumber)
verifyOtpFragment.show(supportFragmentManager, verifyOtpFragment.tag)
}
@@ -77,26 +78,25 @@ class RegisterActivity : AppCompatActivity() {
result.proceedWhen(
doOnSuccess = {
binding.pbLoading.isVisible = false
- binding.btnRegister.isVisible = true
+ binding.btnRegister.isEnabled = true
result.payload?.let {
val token = it.token
- openOtpModal(token, email)
+ openOtpModal(token, email, phoneNumber)
}
},
doOnError = {
binding.pbLoading.isVisible = false
- binding.btnRegister.isVisible = true
+ binding.btnRegister.isEnabled = true
if (it.exception is NoInternetException) {
binding.csvRegister.setState(
ContentState.ERROR_NETWORK_GENERAL,
- "Tidak ada internet!",
+ "No Internet Connection!",
)
} else {
Toasty.error(
this,
getString(
R.string.text_register_failed,
- it.exception?.message.orEmpty(),
),
Toast.LENGTH_SHORT,
true,
@@ -105,7 +105,7 @@ class RegisterActivity : AppCompatActivity() {
},
doOnLoading = {
binding.pbLoading.isVisible = true
- binding.btnRegister.isVisible = false
+ binding.btnRegister.isEnabled = false
},
)
}
@@ -118,7 +118,7 @@ class RegisterActivity : AppCompatActivity() {
tilNoTlp.isVisible = true
tilPassword.isVisible = true
tilConfirmPassword.isVisible = true
- etNoTlp.setText("62")
+ etNoTlp.setText(getString(R.string.default_phone_number_value))
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpFragment.kt b/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpFragment.kt
index d454060..fd5a520 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpFragment.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpFragment.kt
@@ -22,19 +22,24 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
private lateinit var binding: FragmentVerifyOtpBinding
private val verifyOtpViewModel: VerifyOtpViewModel by viewModel()
private lateinit var token: String
+ private lateinit var phoneNumber: String
+ private var countdownTimer: CountDownTimer? = null
companion object {
private const val ARG_TOKEN = "arg_token"
private const val ARG_EMAIL = "arg_email"
+ private const val ARG_PHONE_NUMBER = "arg_phone_number"
fun newInstance(
token: String?,
email: String?,
+ phoneNumber: String?,
): VerifyOtpFragment {
val fragment = VerifyOtpFragment()
val args = Bundle()
args.putString(ARG_TOKEN, token)
args.putString(ARG_EMAIL, email)
+ args.putString(ARG_PHONE_NUMBER, phoneNumber)
fragment.arguments = args
return fragment
}
@@ -55,14 +60,36 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
) {
super.onViewCreated(view, savedInstanceState)
startCountdownTimer()
+ setOnClickListeners()
getOtpViewValue()
val email = arguments?.getString(ARG_EMAIL).toString()
- binding.tvEmailValue.text = email
+ phoneNumber = arguments?.getString(ARG_PHONE_NUMBER).toString()
+ token = arguments?.getString(ARG_TOKEN).toString()
+ binding.tvEmailValue.text = maskEmail(email)
+ }
+
+ private fun maskEmail(email: String): String {
+ val atIndex = email.indexOf('@')
+ if (atIndex <= 1) {
+ return email
+ }
+ val maskedPart = "*".repeat(atIndex - 1)
+ return email[0] + maskedPart + email.substring(atIndex - 3)
+ }
+
+ private fun setOnClickListeners() {
+ binding.tvResendOtp.setOnClickListener {
+ requestResendOtp(token)
+ startCountdownTimer()
+ }
+ binding.tvReqNewOtp.setOnClickListener {
+ requestResendOtpSms(token, phoneNumber)
+ startCountdownTimer()
+ }
}
private fun getOtpViewValue() {
val otpView = binding.otpView
- token = arguments?.getString(ARG_TOKEN).toString()
otpView.setOtpCompletionListener { otp ->
Log.d("Actual Value", otp)
@@ -70,9 +97,6 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
verifyProceed(token, otp)
}
}
- binding.tvResendOtp.setOnClickListener {
- requestResendOtp(token)
- }
}
private fun verifyProceed(
@@ -105,14 +129,12 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
}
private fun requestResendOtp(token: String) {
- Log.d("VerifyOtpFragment", "Requesting resend OTP for token: $token")
verifyOtpViewModel.resendOtpRequest(token).observe(viewLifecycleOwner) { result ->
result.proceedWhen(
doOnSuccess = {
- Log.d("VerifyOtpFragment", "Resend OTP success: ${it.message}")
Toast.makeText(
requireContext(),
- "OTP dikirim ke email!",
+ getString(R.string.text_otp_has_been_sent_to_your_email),
Toast.LENGTH_SHORT,
).show()
it.payload?.let {
@@ -129,6 +151,35 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
}
}
+ private fun requestResendOtpSms(
+ token: String,
+ phoneNumber: String,
+ ) {
+ Log.d("VerifyOtpFragment", "Requesting resend OTP for token: $token")
+ verifyOtpViewModel.resendOtpSmsRequest(token, phoneNumber)
+ .observe(viewLifecycleOwner) { result ->
+ result.proceedWhen(
+ doOnSuccess = {
+ Log.d("VerifyOtpFragment", "Resend OTP success: ${it.message}")
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.text_otp_has_been_sent_to_your_sms),
+ Toast.LENGTH_SHORT,
+ ).show()
+ it.payload?.let {
+ val newToken = it.token
+ if (newToken != null) {
+ this@VerifyOtpFragment.token = newToken
+ }
+ }
+ },
+ doOnError = {
+ Log.d("resendOtpError", "requestResendOtp: Error ${it.exception?.message}")
+ },
+ )
+ }
+ }
+
private fun navigateToLogin() {
startActivity(
Intent(requireActivity(), LoginActivity::class.java).apply {
@@ -138,19 +189,30 @@ class VerifyOtpFragment : BottomSheetDialogFragment() {
}
private fun startCountdownTimer() {
- val countdownTimer =
+ countdownTimer?.cancel()
+
+ countdownTimer =
object : CountDownTimer(60000, 1000) {
@SuppressLint("StringFormatMatches")
override fun onTick(millisUntilFinished: Long) {
- binding.tvResendOtpCoundown.text =
- getString(R.string.text_countdown_otp, millisUntilFinished / 1000)
+ if (isAdded) {
+ binding.tvResendOtpCoundown.text =
+ getString(R.string.text_countdown_otp, millisUntilFinished / 1000)
+ }
}
override fun onFinish() {
- binding.tvResendOtp.isVisible = true
- binding.tvResendOtpCoundown.isVisible = false
+ if (isAdded) {
+ binding.tvResendOtp.isVisible = true
+ binding.tvResendOtpCoundown.isVisible = false
+ }
}
}
- countdownTimer.start()
+ countdownTimer?.start()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ countdownTimer?.cancel()
}
}
diff --git a/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpViewModel.kt b/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpViewModel.kt
index f1c3bab..a2ffdeb 100644
--- a/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpViewModel.kt
+++ b/app/src/main/java/com/kom/skyfly/presentation/verifyotp/VerifyOtpViewModel.kt
@@ -16,4 +16,9 @@ class VerifyOtpViewModel(private val authRepository: AuthRepository) : ViewModel
) = authRepository.doVerifyAccount(token, otp).asLiveData(Dispatchers.IO)
fun resendOtpRequest(token: String) = authRepository.resendOtpRequest(token).asLiveData(Dispatchers.IO)
+
+ fun resendOtpSmsRequest(
+ token: String,
+ phoneNumber: String,
+ ) = authRepository.resendOtpSmsRequest(token, phoneNumber).asLiveData(Dispatchers.IO)
}
diff --git a/app/src/main/java/com/kom/skyfly/utils/ResultWrapper.kt b/app/src/main/java/com/kom/skyfly/utils/ResultWrapper.kt
index 31b3bb6..57fd954 100644
--- a/app/src/main/java/com/kom/skyfly/utils/ResultWrapper.kt
+++ b/app/src/main/java/com/kom/skyfly/utils/ResultWrapper.kt
@@ -4,10 +4,16 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
+import retrofit2.HttpException
import java.io.IOException
+import java.net.HttpURLConnection
class NoInternetException() : IOException()
+class UnAuthorizeException() : Exception()
+
+class ServerErrorException() : Exception()
+
sealed class ResultWrapper(
val payload: T? = null,
val message: String? = null,
@@ -88,10 +94,23 @@ fun proceedFlow(block: suspend () -> T): Flow> {
}
fun Throwable?.parseException(): Exception {
- when (this) {
+ return when (this) {
is IOException -> {
- return NoInternetException()
+ NoInternetException()
+ }
+
+ is HttpException -> {
+ if (this.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ UnAuthorizeException()
+ } else if (this.code() == HttpURLConnection.HTTP_INTERNAL_ERROR) {
+ ServerErrorException()
+ } else {
+ Exception(this)
+ }
+ }
+
+ else -> {
+ Exception(this)
}
- else -> return Exception(this)
}
}
diff --git a/app/src/main/java/com/kom/skyfly/utils/SetVisibilityExt.kt b/app/src/main/java/com/kom/skyfly/utils/SetVisibilityExt.kt
new file mode 100644
index 0000000..4d84e4d
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/utils/SetVisibilityExt.kt
@@ -0,0 +1,7 @@
+package com.kom.skyfly.utils
+
+import android.view.View
+
+fun View.setVisible(isVisible: Boolean) {
+ visibility = if (isVisible) View.VISIBLE else View.GONE
+}
diff --git a/app/src/main/java/com/kom/skyfly/utils/TextWatcherExt.kt b/app/src/main/java/com/kom/skyfly/utils/TextWatcherExt.kt
new file mode 100644
index 0000000..a651be5
--- /dev/null
+++ b/app/src/main/java/com/kom/skyfly/utils/TextWatcherExt.kt
@@ -0,0 +1,31 @@
+package com.kom.skyfly.utils
+
+import android.text.Editable
+import android.text.TextWatcher
+import android.widget.TextView
+
+fun TextView.afterTextChanged(afterTextChanged: (String) -> Unit) {
+ this.addTextChangedListener(
+ object : TextWatcher {
+ override fun beforeTextChanged(
+ s: CharSequence?,
+ start: Int,
+ count: Int,
+ after: Int,
+ ) {
+ }
+
+ override fun onTextChanged(
+ s: CharSequence?,
+ start: Int,
+ before: Int,
+ count: Int,
+ ) {
+ }
+
+ override fun afterTextChanged(editable: Editable?) {
+ afterTextChanged.invoke(editable.toString())
+ }
+ },
+ )
+}
diff --git a/app/src/main/res/layout/activity_checkout_ticket.xml b/app/src/main/res/layout/activity_checkout_ticket.xml
index 54fded1..5783290 100644
--- a/app/src/main/res/layout/activity_checkout_ticket.xml
+++ b/app/src/main/res/layout/activity_checkout_ticket.xml
@@ -1,5 +1,5 @@
-
-
+ android:layout_height="match_parent">
-
+
-
+
-
+
+
+ android:layout_marginHorizontal="8dp"
+ android:layout_marginVertical="16dp"
+ android:visibility="gone"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/layout_header"
+ tools:visibility="visible">
-
+ android:layout_height="wrap_content">
-
+
-
+
-
+
-
+
+
+
+ android:layout_height="0dp"
+ android:layout_margin="8dp"
+ app:layout_constraintBottom_toTopOf="@id/cv_btn_proceed_to_payment"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/layout_header">
-
+ android:paddingVertical="4dp">
+
+
+
+
+
+
+
-
+
+
-
+ android:padding="16dp">
+
+
-
+
+
-
+
+
+
diff --git a/app/src/main/res/layout/activity_choose_seat.xml b/app/src/main/res/layout/activity_choose_seat.xml
index da57218..1024ce4 100644
--- a/app/src/main/res/layout/activity_choose_seat.xml
+++ b/app/src/main/res/layout/activity_choose_seat.xml
@@ -1,5 +1,5 @@
-
-
+ android:layout_height="match_parent">
-
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@id/fl_btn_save"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
-
+ android:layout_height="wrap_content">
-
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/layout_header">
-
+ android:layout_height="50dp"
+ android:background="@color/md_theme_primaryFixed_mediumContrast">
+
+
+
+
+
+
+
-
+
+
+ android:layout_height="wrap_content">
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
-
+
-
-
+
-
-
-
+
+
-
-
+
+
+
+
-
-
-
\ No newline at end of file
+ android:padding="16dp"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_detail_home.xml b/app/src/main/res/layout/activity_detail_home.xml
index f41b139..2d5c6c3 100644
--- a/app/src/main/res/layout/activity_detail_home.xml
+++ b/app/src/main/res/layout/activity_detail_home.xml
@@ -6,29 +6,88 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.home.detail_home.DetailHomeActivity">
+
-
+ app:layout_constraintTop_toBottomOf="@id/header_detail_ticket_home"
+ app:layout_constraintBottom_toTopOf="@id/btn_select_ticket">
+
+ android:layout_height="wrap_content"
+ android:padding="16dp">
+
+ app:layout_constraintTop_toTopOf="parent" />
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_flight_detail.xml b/app/src/main/res/layout/activity_flight_detail.xml
index 727dd5e..ba11f72 100644
--- a/app/src/main/res/layout/activity_flight_detail.xml
+++ b/app/src/main/res/layout/activity_flight_detail.xml
@@ -1,50 +1,90 @@
-
-
-
+
+ app:layout_constraintTop_toTopOf="parent">
+
-
-
-
-
+
+
+ android:layout_height="wrap_content">
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -73,7 +113,6 @@
android:indeterminate="true"
android:visibility="gone" />
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_flight_detail_history.xml b/app/src/main/res/layout/activity_flight_detail_history.xml
index eb8908d..f8f9092 100644
--- a/app/src/main/res/layout/activity_flight_detail_history.xml
+++ b/app/src/main/res/layout/activity_flight_detail_history.xml
@@ -1,5 +1,5 @@
-
+
-
-
+
+
+ tools:visibility="gone" />
-
+ android:background="@color/md_theme_primary"
+ android:padding="16dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
-
+
-
+
-
+
+
+ android:layout_margin="8dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/header_flight_details"
+ tools:visibility="visible">
-
+ android:layout_height="wrap_content">
-
+
-
+
-
-
-
+
+
+ android:layout_height="0dp"
+ android:layout_margin="8dp"
+ app:layout_constraintBottom_toTopOf="@id/cv_btn_proceed_to_payment"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/header_flight_details">
-
+ android:paddingVertical="4dp">
+
+
+
+
-
+
+
+
+
-
+ android:padding="16dp">
-
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index a7062ad..536781c 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,5 +1,5 @@
-
-
-
-
+ android:layout_height="wrap_content">
-
+
-
+
-
+
-
+
+
+
-
+ android:padding="16dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/tv_forget_password">
+
+
-
+
+
+
-
+ android:layout_marginHorizontal="16dp"
+ android:text="@string/tv_not_have_account"
+ android:textAlignment="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/fl_btn_login" />
-
+
-
-
+
+
diff --git a/app/src/main/res/layout/activity_notification_detail.xml b/app/src/main/res/layout/activity_notification_detail.xml
index 8c34307..175594d 100644
--- a/app/src/main/res/layout/activity_notification_detail.xml
+++ b/app/src/main/res/layout/activity_notification_detail.xml
@@ -41,15 +41,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_notification_type" />
-
+
-
+
+
+
+
+
+
+
+
+ android:padding="16dp"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_search_result.xml b/app/src/main/res/layout/activity_search_result.xml
index 64231f6..adea52d 100644
--- a/app/src/main/res/layout/activity_search_result.xml
+++ b/app/src/main/res/layout/activity_search_result.xml
@@ -9,20 +9,36 @@
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/header_flight_search_result"
+ tools:listitem="@layout/layout_date" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/v_calender">
+ android:text="@string/text_filter"
+ android:textColor="@android:color/black"
+ android:textSize="16sp" />
+
+ tools:listitem="@layout/layout_item_card_search_result" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml
index a8e8f34..8ac9fed 100644
--- a/app/src/main/res/layout/fragment_account.xml
+++ b/app/src/main/res/layout/fragment_account.xml
@@ -44,6 +44,7 @@
app:layout_constraintTop_toBottomOf="@id/layout_profile_user" />
+ tools:context=".presentation.bottomsheetsdialog.NotLoginBottomSheets">
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/til_family_name">
+ tools:context=".presentation.bottomsheetsdialog.NotLoginBottomSheets">
@@ -68,7 +69,7 @@
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="32dp"
android:background="@drawable/btn_rounded"
- android:text="@string/text_issue_ticket"
+ android:text="@string/text_ticket_details"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/fragment_calendar_view.xml b/app/src/main/res/layout/fragment_calendar_view.xml
index f9736e3..fa7e3df 100644
--- a/app/src/main/res/layout/fragment_calendar_view.xml
+++ b/app/src/main/res/layout/fragment_calendar_view.xml
@@ -2,6 +2,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="16dp">
@@ -20,7 +21,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:text="@string/text_date_daparture"
+ android:text="@string/text_start_date"
android:textAlignment="center"
app:layout_constraintEnd_toStartOf="@id/vertical_line"
app:layout_constraintStart_toStartOf="parent"
@@ -32,7 +33,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/font_open_sans_semi_bold"
- android:text="@string/text_date_departure"
+ android:text="@string/text_select_date"
+ android:textColor="@color/md_theme_primary"
android:textAlignment="center"
android:textSize="16sp"
android:textStyle="bold"
@@ -55,7 +57,7 @@
android:id="@+id/tv_text_back_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/text_back_title"
+ android:text="@string/text_end_date"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/vertical_line"
@@ -67,7 +69,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/font_open_sans_semi_bold"
- android:text="@string/text_date_departure"
+ android:text="@string/text_select_date"
+ android:textColor="@color/md_theme_primary"
android:textAlignment="center"
android:textSize="16sp"
android:textStyle="bold"
@@ -81,7 +84,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="@font/font_open_sans_semi_bold"
- android:text="@string/text_month"
+ tools:text="@string/text_month"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/fragment_history.xml b/app/src/main/res/layout/fragment_history.xml
index ec39898..6cab901 100644
--- a/app/src/main/res/layout/fragment_history.xml
+++ b/app/src/main/res/layout/fragment_history.xml
@@ -1,106 +1,107 @@
-
-
+ android:layout_height="match_parent">
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+ android:layout_marginTop="16dp"
+ android:background="@drawable/btn_rounded"
+ android:backgroundTint="@color/white"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/layout_header_order_history">
+
+
+
+
+
+
+
+
-
+ android:layout_height="wrap_content">
-
-
-
+
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index bba5e0b..88c96de 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -34,8 +34,9 @@
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/text_hai_mau_ke_mana"
- android:textSize="22sp"
+ android:textSize="25sp"
android:textStyle="bold"
+ android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -82,8 +83,8 @@
app:layout_constraintEnd_toEndOf="@id/layout_select_destination"
app:layout_constraintTop_toTopOf="@id/tv_text_pulang_pergi"
app:thumbTint="@color/white"
- app:track="@drawable/switch_track"
- app:trackTint="@color/md_theme_primaryFixed_mediumContrast" />
+ app:trackTint="@color/grey"
+ app:track="@drawable/switch_track"/>
@@ -110,6 +112,7 @@
android:id="@+id/tv_departure"
android:layout_width="130dp"
android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
android:text="@string/text_departure_date_value"
android:ellipsize="end"
android:textColor="@color/md_theme_primary"
@@ -122,10 +125,12 @@
android:layout_width="0dp"
android:layout_height="2dp"
android:layout_marginVertical="8dp"
+ android:layout_marginEnd="8dp"
android:background="@color/grey"
- app:layout_constraintEnd_toEndOf="@id/tv_departure"
+ app:layout_constraintEnd_toStartOf="@id/iv_return"
app:layout_constraintStart_toStartOf="@id/tv_departure"
app:layout_constraintTop_toBottomOf="@id/tv_departure" />
+
+ app:layout_constraintTop_toBottomOf="@id/tv_return_title"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ app:layout_constraintTop_toBottomOf="@+id/tv_return" />
@@ -193,6 +206,7 @@
android:id="@+id/tv_passengers"
android:layout_width="130dp"
android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
tools:text="@string/text_add_passenger"
android:textColor="@color/md_theme_primary"
android:ellipsize="end"
@@ -205,14 +219,15 @@
android:layout_width="0dp"
android:layout_height="2dp"
android:layout_marginVertical="8dp"
+ android:layout_marginEnd="8dp"
android:background="@color/grey"
- app:layout_constraintEnd_toEndOf="@id/v_select_departure_time"
+ app:layout_constraintEnd_toStartOf="@id/iv_seat"
app:layout_constraintStart_toStartOf="@id/v_select_departure_time"
app:layout_constraintTop_toBottomOf="@id/tv_passengers" />
@@ -221,23 +236,26 @@
android:id="@+id/tv_seat_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
android:text="@string/text_seat_class_title"
android:textSize="16sp"
+ android:textColor="@color/darkGrey"
app:layout_constraintTop_toTopOf="@id/tv_passenger_title"
app:layout_constraintStart_toEndOf="@id/iv_seat" />
+ app:layout_constraintEnd_toEndOf="parent" />
+
@@ -264,8 +284,8 @@
android:id="@+id/tv_destination_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="Destinasi Favorit"
+ android:layout_marginTop="32dp"
+ android:text="@string/text_favorite_destination"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/rv_category"
@@ -277,6 +297,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
+ android:layout_marginTop="8dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintTop_toBottomOf="@id/tv_destination_title"
diff --git a/app/src/main/res/layout/fragment_notification.xml b/app/src/main/res/layout/fragment_notification.xml
index 7e25abe..be0498c 100644
--- a/app/src/main/res/layout/fragment_notification.xml
+++ b/app/src/main/res/layout/fragment_notification.xml
@@ -15,11 +15,11 @@
android:id="@+id/tv_title_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="8dp"
- android:layout_marginTop="32dp"
+ android:layout_margin="16dp"
android:text="@string/text_notification"
android:textSize="25sp"
android:textStyle="bold"
+ android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/app/src/main/res/layout/fragment_passenger.xml b/app/src/main/res/layout/fragment_passenger.xml
index 2ccb78f..1d4a678 100644
--- a/app/src/main/res/layout/fragment_passenger.xml
+++ b/app/src/main/res/layout/fragment_passenger.xml
@@ -18,7 +18,7 @@
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_margin="8dp"
- android:src="@drawable/ic_remove"
+ android:src="@drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -81,10 +81,11 @@
android:layout_width="45dp"
android:layout_height="30dp"
android:layout_marginEnd="4dp"
+ android:textAlignment="center"
app:layout_constraintEnd_toStartOf="@id/iv_btn_plus_adult"
app:layout_constraintTop_toTopOf="@id/iv_adult_icon"
- tools:text="0"
- android:textAlignment="center"/>
+ tools:text="0" />
+
+ tools:text="0" />
+
+
+ tools:text="0" />
+
-
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/tv_age_desc_baby" />
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index da4afcf..14b1c23 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -1,7 +1,7 @@
+ app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toBottomOf="@+id/layout_home_search_bar" />
+ tools:visibility="visible" />
+ tools:listitem="@layout/layout_item_airport_search"
+ tools:visibility="visible" />
+
+
diff --git a/app/src/main/res/layout/fragment_seat_class.xml b/app/src/main/res/layout/fragment_seat_class.xml
index 6b97867..8c0a276 100644
--- a/app/src/main/res/layout/fragment_seat_class.xml
+++ b/app/src/main/res/layout/fragment_seat_class.xml
@@ -1,51 +1,61 @@
+ app:cardCornerRadius="16dp"
+ tools:context=".presentation.home.seatclass.SeatClassFragment">
+
+
+ app:layout_constraintTop_toTopOf="parent" />
+
+
-
+
+
+ app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/item_form_passenger_biodata.xml b/app/src/main/res/layout/item_form_passenger_biodata.xml
index 0831abd..628ac3d 100644
--- a/app/src/main/res/layout/item_form_passenger_biodata.xml
+++ b/app/src/main/res/layout/item_form_passenger_biodata.xml
@@ -33,9 +33,9 @@
android:text="@string/text_passenger_type"
android:textColor="@color/white"
android:textSize="18sp"
- app:layout_constraintStart_toStartOf="parent"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -119,7 +119,7 @@
app:layout_constraintTop_toTopOf="parent"
app:thumbTint="@color/white"
app:track="@drawable/switch_track"
- app:trackTint="@color/md_theme_primaryFixed_mediumContrast"
+ app:trackTint="@color/grey"
/>
@@ -266,8 +266,8 @@
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="6dp"
- android:enabled="false"
android:background="@drawable/layout_outline_border"
+ android:enabled="false"
android:hint="@string/text_passport_expiry_date"
app:layout_constraintEnd_toStartOf="@id/iv_calendar_valid_until"
app:layout_constraintStart_toStartOf="parent"
diff --git a/app/src/main/res/layout/item_order_history.xml b/app/src/main/res/layout/item_order_history.xml
index 3d00dc1..3b28fb5 100644
--- a/app/src/main/res/layout/item_order_history.xml
+++ b/app/src/main/res/layout/item_order_history.xml
@@ -1,13 +1,13 @@
+ app:cardElevation="4dp">
+ app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toBottomOf="@id/tv_status"
+ app:tint="@color/darkGrey" />
+ app:layout_constraintTop_toTopOf="@id/iv_departure_location" />
+ app:layout_constraintTop_toBottomOf="@id/tv_departure_location" />
+ app:layout_constraintTop_toBottomOf="@id/tv_departure_date" />
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@id/tv_departure_location" />
+ app:layout_constraintStart_toStartOf="@id/tv_flight_duration"
+ app:layout_constraintTop_toBottomOf="@id/tv_flight_duration" />
+ app:layout_constraintTop_toTopOf="@id/iv_departure_location"
+ app:tint="@color/darkGrey" />
+ app:layout_constraintTop_toTopOf="@id/iv_destination" />
+ app:layout_constraintTop_toBottomOf="@id/tv_destination" />
+ app:layout_constraintTop_toBottomOf="@id/tv_arrival_date" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/tv_departure_time" />
+ app:layout_constraintTop_toBottomOf="@id/v_line" />
+ app:layout_constraintTop_toTopOf="@id/tv_title_booking_code" />
+ app:layout_constraintTop_toBottomOf="@id/v_line" />
+ app:layout_constraintTop_toBottomOf="@id/tv_title_booking_code" />
+ app:layout_constraintTop_toBottomOf="@id/tv_title_class" />
diff --git a/app/src/main/res/layout/layout_date.xml b/app/src/main/res/layout/layout_date.xml
index f954b0a..5100df9 100644
--- a/app/src/main/res/layout/layout_date.xml
+++ b/app/src/main/res/layout/layout_date.xml
@@ -1,30 +1,55 @@
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
-
+
-
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_detail_history_shimmer.xml b/app/src/main/res/layout/layout_detail_history_shimmer.xml
new file mode 100644
index 0000000..6989970
--- /dev/null
+++ b/app/src/main/res/layout/layout_detail_history_shimmer.xml
@@ -0,0 +1,312 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_detail_ticket_shimmer.xml b/app/src/main/res/layout/layout_detail_ticket_shimmer.xml
new file mode 100644
index 0000000..0a6db1c
--- /dev/null
+++ b/app/src/main/res/layout/layout_detail_ticket_shimmer.xml
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_flight_detail_shimmer.xml b/app/src/main/res/layout/layout_flight_detail_shimmer.xml
new file mode 100644
index 0000000..ae6e5aa
--- /dev/null
+++ b/app/src/main/res/layout/layout_flight_detail_shimmer.xml
@@ -0,0 +1,315 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_flight_details.xml b/app/src/main/res/layout/layout_flight_details.xml
index d5e178a..9f7cc9d 100644
--- a/app/src/main/res/layout/layout_flight_details.xml
+++ b/app/src/main/res/layout/layout_flight_details.xml
@@ -1,30 +1,31 @@
+ android:layout_marginBottom="8dp">
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="@string/text_unpaid" />
+ app:layout_constraintTop_toBottomOf="@+id/tv_payment_code_title" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_title_booking_code"
+ tools:text="@string/text_time" />
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_departure_time"
+ tools:text="@string/text_date" />
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_departure_date"
+ tools:text="@string/text_example_airport" />
+ app:layout_constraintTop_toTopOf="@id/tv_detail_departure_airport"
+ tools:text="@string/text_example_terminal" />
+ app:layout_constraintTop_toBottomOf="@id/v_line_1"
+ tools:text="@string/text_detail_airline" />
+ app:layout_constraintTop_toTopOf="@id/tv_detail_airline"
+ tools:text="@string/text_example_class" />
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_airline"
+ tools:text="@string/text_example_flight_number" />
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/tv_title_information"
+ tools:text="@string/text_title_passenger" />
+
-
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/tv_title_passenger"
+ tools:text="@string/text_title_id" />
+ app:layout_constraintTop_toBottomOf="@id/tv_citizenship" />
+ app:layout_constraintTop_toBottomOf="@id/v_line_2"
+ tools:text="@string/text_arrival_time" />
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_arrival_time"
+ tools:text="@string/text_date" />
+ app:layout_constraintTop_toBottomOf="@id/tv_detail_arrival_date"
+ tools:text="@string/text_example_destination_airport" />
+ app:layout_constraintTop_toBottomOf="@id/cv_flight_details"
+ app:layout_constraintBottom_toBottomOf="parent">
+ app:layout_constraintTop_toBottomOf="@id/tv_title_price_details"
+ tools:text="@string/text_total_by_age_group" />
+ app:layout_constraintTop_toBottomOf="@id/tv_total_by_age_group_adult"
+ tools:text="@string/text_2_children" />
+ app:layout_constraintTop_toBottomOf="@id/tv_total_by_age_group_child"
+ tools:text="@string/text_2_baby" />
+ app:layout_constraintTop_toTopOf="@id/tv_total_by_age_group_baby"
+ tools:text="@string/text_total_price_by_age_group" />
+ app:layout_constraintTop_toTopOf="@id/tv_total_by_age_group_adult"
+ tools:text="@string/text_total_price_by_age_group" />
+ app:layout_constraintTop_toTopOf="@id/tv_total_by_age_group_child"
+ tools:text="@string/text_total_price_by_age_group" />
+ app:layout_constraintTop_toTopOf="@id/tv_title_tax"
+ tools:text="@string/text_example_tax" />
+ app:layout_constraintTop_toTopOf="@id/tv_title_total"
+ tools:text="@string/text_total_price" />
diff --git a/app/src/main/res/layout/layout_form_customer_biodata.xml b/app/src/main/res/layout/layout_form_customer_biodata.xml
index 0965076..17c5414 100644
--- a/app/src/main/res/layout/layout_form_customer_biodata.xml
+++ b/app/src/main/res/layout/layout_form_customer_biodata.xml
@@ -87,7 +87,7 @@
app:layout_constraintTop_toTopOf="parent"
app:thumbTint="@color/white"
app:track="@drawable/switch_track"
- app:trackTint="@color/md_theme_primaryFixed_mediumContrast" />
+ app:trackTint="@color/grey" />
diff --git a/app/src/main/res/layout/layout_header.xml b/app/src/main/res/layout/layout_header.xml
index b515873..858db0d 100644
--- a/app/src/main/res/layout/layout_header.xml
+++ b/app/src/main/res/layout/layout_header.xml
@@ -2,34 +2,35 @@
+ android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:background="@color/md_theme_primary">
+ app:layout_constraintStart_toEndOf="@id/iv_back"
+ app:layout_constraintTop_toTopOf="@id/iv_back" />
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_header_bar_detail_ticket_home.xml b/app/src/main/res/layout/layout_header_bar_detail_ticket_home.xml
index 4fece45..8f46c37 100644
--- a/app/src/main/res/layout/layout_header_bar_detail_ticket_home.xml
+++ b/app/src/main/res/layout/layout_header_bar_detail_ticket_home.xml
@@ -1,33 +1,36 @@
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:backgroundTint="@color/md_theme_primary">
+
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+ app:layout_constraintTop_toTopOf="parent" />
+
+ android:textSize="20sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/iv_back_btn_detail_ticket_home"
+ app:layout_constraintTop_toTopOf="parent" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_header_bar_search_result.xml b/app/src/main/res/layout/layout_header_bar_search_result.xml
index d8a2893..9debc8b 100644
--- a/app/src/main/res/layout/layout_header_bar_search_result.xml
+++ b/app/src/main/res/layout/layout_header_bar_search_result.xml
@@ -1,21 +1,25 @@
+ android:backgroundTint="@color/md_theme_primary">
+
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+
+
diff --git a/app/src/main/res/layout/layout_header_order_history.xml b/app/src/main/res/layout/layout_header_order_history.xml
index 254fd44..6e2e01c 100644
--- a/app/src/main/res/layout/layout_header_order_history.xml
+++ b/app/src/main/res/layout/layout_header_order_history.xml
@@ -10,7 +10,8 @@
android:layout_height="wrap_content"
android:text="@string/text_title_order_history"
android:textStyle="bold"
- android:textSize="26sp"
+ android:textSize="24sp"
+ android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
diff --git a/app/src/main/res/layout/layout_home_search_bar.xml b/app/src/main/res/layout/layout_home_search_bar.xml
index 485e3bb..8eb67d3 100644
--- a/app/src/main/res/layout/layout_home_search_bar.xml
+++ b/app/src/main/res/layout/layout_home_search_bar.xml
@@ -31,12 +31,12 @@
android:layout_marginStart="16dp"
android:src="@drawable/ic_search"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@id/et_search"
+ app:layout_constraintEnd_toStartOf="@id/et_home_search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_airport_search.xml b/app/src/main/res/layout/layout_item_airport_search.xml
index bbbac50..d8c69c4 100644
--- a/app/src/main/res/layout/layout_item_airport_search.xml
+++ b/app/src/main/res/layout/layout_item_airport_search.xml
@@ -1,32 +1,35 @@
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ android:layout_height="match_parent">
+
+ tools:text="Jakarta" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/tv_flight_home_recent_search" />
diff --git a/app/src/main/res/layout/layout_item_card_search_result.xml b/app/src/main/res/layout/layout_item_card_search_result.xml
index 162e6c9..3de9a13 100644
--- a/app/src/main/res/layout/layout_item_card_search_result.xml
+++ b/app/src/main/res/layout/layout_item_card_search_result.xml
@@ -4,31 +4,36 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp">
+ android:layout_marginTop="8dp"
+ app:cardCornerRadius="10dp"
+ app:cardElevation="2dp">
+ android:layout_height="match_parent"
+ android:padding="8dp">
+ tools:text="@string/text_hour" />
+ tools:text="@string/text_jkt" />
+ tools:text="@string/text_flight_duration" />
+ tools:text="@string/text_direct" />
+ tools:text="@string/text_hour" />
+ tools:text="@string/text_mlb" />
+ android:layout_height="wrap_content">
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Jakarta -> Melbourne (4h 0m)" />
+ app:layout_constraintTop_toBottomOf="@id/tv_flight_destination_detail_ticket"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toTopOf="@id/tv_departure_time_detail_ticket" />
+ app:layout_constraintTop_toBottomOf="@id/tv_departure_time_detail_ticket"
+ tools:text="3 Maret 2023" />
-
-
+ tools:text="@string/text_baggage" />
+ app:layout_constraintTop_toBottomOf="@id/tv_arrival_time_detail_ticket"
+ tools:text="3 Maret 2023" />
-
-
+
+
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:elevation="4dp"
+ app:layout_constraintBottom_toTopOf="@+id/v_select_seatclass"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_ticket_grid.xml b/app/src/main/res/layout/layout_item_ticket_grid.xml
index 17d7ed0..7eb76d9 100644
--- a/app/src/main/res/layout/layout_item_ticket_grid.xml
+++ b/app/src/main/res/layout/layout_item_ticket_grid.xml
@@ -46,7 +46,7 @@
android:fontFamily="@font/font_open_sans_semi_bold"
android:paddingVertical="4dp"
android:paddingHorizontal="16dp"
- android:text="Limited!"
+ tools:text="Limited!"
android:textAlignment="center"
android:textColor="@android:color/white"
android:visibility="gone"
@@ -63,7 +63,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="Jakarta -> Bangkok"
+ tools:text="Jakarta -> Bangkok"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fl_image_container" />
@@ -73,41 +73,43 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
- android:text="AirAsia"
+ tools:text="AirAsia"
android:textColor="@color/md_theme_primary"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_card_destination" />
-
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/tv_card_airplane" />
+ android:ellipsize="end"
+ android:maxLines="1"
+ app:layout_constraintStart_toStartOf="@id/tv_card_start_from"
+ app:layout_constraintTop_toBottomOf="@id/tv_card_start_from"
+ app:layout_constraintEnd_toEndOf="parent"/>
diff --git a/app/src/main/res/layout/layout_profile_user.xml b/app/src/main/res/layout/layout_profile_user.xml
index 349fe5f..f22b581 100644
--- a/app/src/main/res/layout/layout_profile_user.xml
+++ b/app/src/main/res/layout/layout_profile_user.xml
@@ -133,6 +133,36 @@
android:textSize="18sp" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_select_destination.xml b/app/src/main/res/layout/layout_select_destination.xml
index d06cc87..a0523b0 100644
--- a/app/src/main/res/layout/layout_select_destination.xml
+++ b/app/src/main/res/layout/layout_select_destination.xml
@@ -11,7 +11,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
+ app:layout_constraintStart_toEndOf="@id/tv_from"
+ app:layout_constraintTop_toTopOf="@id/iv_departures">
+ android:text="@string/text_select_origin"
+ android:textColor="@color/md_theme_primary"
+ android:textSize="16sp" />
-
-
+ app:layout_constraintStart_toEndOf="@id/tv_to"
+ app:layout_constraintTop_toTopOf="@id/iv_destination">
+ android:text="@string/text_select_destination"
+ android:textColor="@color/md_theme_primary"
+ android:textSize="16sp" />
diff --git a/app/src/main/res/layout/layout_ticket_shimmer.xml b/app/src/main/res/layout/layout_ticket_shimmer.xml
new file mode 100644
index 0000000..0c44db4
--- /dev/null
+++ b/app/src/main/res/layout/layout_ticket_shimmer.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 30380b7..c10ee42 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -27,7 +27,7 @@
Enter OTP
Enter the 6-digit code sent to
Resend OTP in 60 seconds
- Request a new code via Email
+ Request a new code via SMS
Full name cannot be empty!
Email cannot be empty!
Enter a valid format!
@@ -74,21 +74,21 @@
- Dr.
- Prof.
- Booking History
+ Flight History
Filter
- Unpaid
- Yogyakarta
+ UNPAID
+ Los Angeles
Enter date or birth
19:10
4h 0m
- Booking Code:
+ Booking Code :
Class:
IDR 4,000,000
974974004
Business
Flight Details
Departure
- Soekarno Hatta -
+ Soekarno Hatta - Terminal 3
Terminal 1A Domestic
Jet Air -
JT - 203
@@ -128,7 +128,7 @@
Edit Profile
Settings
Logout
- v1.0.0
+ v1.3.2
Komang Yuda Saputra
komangyuda@example.com
Welcome to SkyFly!
@@ -164,7 +164,7 @@
Select date
Select date
Return
- Passenger
+ Passengers
Seat Class
(Under 2 years)
Baby
@@ -174,7 +174,7 @@
Adult
Save
Hi, Where are you going?
- Search Flights
+ Search Flights
Passenger 1 - Adult
Data is still empty!
No internet connection!
@@ -206,7 +206,7 @@
Change password successfully!
Are you sure you want to change password?
Add passenger
- Select Seat class
+ Seat Class
Phone number must start with \"62\"
Reset password link has been sent, please check your email.
Passport expiry date
@@ -218,9 +218,75 @@
2 Baby
Issue Ticket
Congratulation!
- Ticket Payment Successful
+ Your ticket has been successfully generated. Check ticket details for more information.
UNPAID
PAID
CANCELLED
Home
+ "%1$s - "
+ Payment Complete
+ Payment Cancelled
+ Ticket Detail
+ %1$s -
+ "%1$s - "
+ OTP has been sent to your email!
+ OTP has been sent to your SMS!
+ 62
+ Loading…
+ Empty Seat
+ Error
+ Server error. Please try again later!
+ Server error. Please try again later!
+ Server error. Please try again later!
+ Session expired, please login again!
+ %1$s - %2$s Seat
+ %1$s Adult
+ %1$s Child
+ %1$s Baby
+
+ All passengers have been assigned seats.
+ No seat selected.
+ Invalid seat selected.
+ Seat is Booked!
+ Seat is Reserved!
+ Select Dates
+ -
+ %2$s - %3$s)]]>
+ Rabu
+ 07:00
+ JKT
+ Direct
+ MLB
+ Select
+ MLG]]>
+ - 2 Penumpang - Economy
+ Select origin
+ Select destination
+ Start From
+ -
+ "%1$s - "
+ Start Date
+ End Date
+ %1$s Adult
+ %1$s Child
+ %1$s Baby
+ Select Date
+ MLB - 2 Passengers - Economy]]>
+ Flight Detail
+ "VA Number : "
+ Cancel Transaction
+ Are you sure you want to cancel transaction?
+ Next
+ Payment
+ Ticket Detail
+ %1$s - %2$s
+ No Tickets Available For the Selected Date
+ Booker\'s Biodata
+ %1$s - %2$s
+ Total
+ %2$s - %3$s Passengers - %4$s]]>
+ %2$s (%3$s)]]>
+ Flight Detail
+ Family Name
+ Enter Family Name
diff --git a/app/src/test/java/com/kom/skyfly/ExampleUnitTest.kt b/app/src/test/java/com/kom/skyfly/ExampleUnitTest.kt
deleted file mode 100644
index fdc098a..0000000
--- a/app/src/test/java/com/kom/skyfly/ExampleUnitTest.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.kom.skyfly
-
-import org.junit.Assert.*
-import org.junit.Test
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/auth/AuthItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/auth/AuthItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..54563d4
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/auth/AuthItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,175 @@
+package com.kom.skyfly.data.datasource.auth
+
+import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordResponse
+import com.kom.skyfly.data.source.network.model.login.LoginResponse
+import com.kom.skyfly.data.source.network.model.register.RegisterResponse
+import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpResponse
+import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpSmsRequest
+import com.kom.skyfly.data.source.network.model.userprofile.UserProfileResponse
+import com.kom.skyfly.data.source.network.model.verifyaccount.VerifyAccountRequest
+import com.kom.skyfly.data.source.network.model.verifyaccount.VerifyAccountResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class AuthItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var service: SkyFlyApiService
+ private lateinit var dataSource: AuthDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ dataSource = AuthDataSourceImpl(service)
+ }
+
+ @Test
+ fun doLogin() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.login(any()) } returns mockResponse
+ val actualResult = dataSource.doLogin("example@gmail.com", "examplePassword")
+ coVerify { service.login(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun doLogin_exception() {
+ runTest {
+ coEvery { service.login(any()) } throws Exception("Login failed")
+
+ var thrownException: Throwable? = null
+ try {
+ kotlinx.coroutines.test.runTest {
+ dataSource.doLogin("example@gmail.com", "examplePassword")
+ }
+ } catch (e: Throwable) {
+ thrownException = e
+ }
+ assertEquals("Login failed", thrownException?.message)
+ }
+ }
+
+ @Test
+ fun doRegister() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.register(any()) } returns mockResponse
+ val actualResult = dataSource.doRegister("exampleFullName", "example@gmail.com", "627848758844", "examplePassword")
+ coVerify { service.register(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun doRegister_exception() {
+ runTest {
+ coEvery { service.register(any()) } throws Exception("register failed")
+
+ var thrownException: Throwable? = null
+ try {
+ kotlinx.coroutines.test.runTest {
+ dataSource.doRegister("Komang", "example@gmail.com", "6267374874", "examplePassword")
+ }
+ } catch (e: Throwable) {
+ thrownException = e
+ }
+ assertEquals("register failed", thrownException?.message)
+ }
+ }
+
+ @Test
+ fun doVerifyAccount() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery {
+ service.verifyAccount(
+ any(),
+ any(),
+ )
+ } returns mockResponse
+ val actualResult = dataSource.doVerifyAccount("eyfejngekjrbgelrjnf", "56453")
+ coVerify { service.verifyAccount(any(), any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun forgetPassword() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery {
+ service.forgetPassword(
+ any(),
+ )
+ } returns mockResponse
+ val actualResult = dataSource.forgetPassword("example@gmail.com")
+ coVerify { service.forgetPassword(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun resendOtpRequest() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.resendOtp(any()) } returns mockResponse
+ val actualResult = dataSource.resendOtpRequest("eyokmeengeurebgepfepfejf")
+ coVerify { service.resendOtp(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun resendOtpSmsRequest() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.resendOtpSms(any(), any()) } returns mockResponse
+ val actualResult = dataSource.resendOtpSmsRequest("eyokmeengeurebgepfepfejf", "62674478474")
+ coVerify { service.resendOtpSms(any(), any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun resendOtpSmsRequest_exception() {
+ runTest {
+ coEvery { service.resendOtpSms(any(), any()) } throws Exception("resendOtpSms failed")
+
+ var thrownException: Throwable? = null
+ try {
+ kotlinx.coroutines.test.runTest {
+ dataSource.resendOtpSmsRequest("eysjngknef", "6267374874")
+ }
+ } catch (e: Throwable) {
+ thrownException = e
+ }
+ assertEquals("resendOtpSms failed", thrownException?.message)
+ }
+ }
+
+ @Test
+ fun isUserLoggedIn() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.getUserProfile() } returns mockResponse
+
+ val actualResult = dataSource.isUserLoggedIn()
+
+ coVerify { service.getUserProfile() }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/flightseat/FlightSeatItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/flightseat/FlightSeatItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..4ff27cd
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/flightseat/FlightSeatItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,40 @@
+package com.kom.skyfly.data.datasource.flightseat
+
+import com.kom.skyfly.data.source.network.model.flightseat.FlightSeatResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class FlightSeatItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var service: SkyFlyApiService
+ private lateinit var dataSource: FlightSeatDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ dataSource = FlightSeatDataSourceImpl(service)
+ }
+
+ @Test
+ fun getAllFlightSeat() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.getAllFlightSeat(any()) } returns mockResponse
+ val actualResult = dataSource.getAllFlightSeat("fefjehgeof")
+ coVerify { service.getAllFlightSeat(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/history/HistoryDataSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/history/HistoryDataSourceImplTest.kt
new file mode 100644
index 0000000..a97b71e
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/history/HistoryDataSourceImplTest.kt
@@ -0,0 +1,47 @@
+package com.kom.skyfly.data.datasource.history
+
+import com.kom.skyfly.data.source.network.model.history.HistoryResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class HistoryDataSourceImplTest {
+ @MockK
+ lateinit var service: SkyFlyApiService
+ private lateinit var dataSource: HistoryDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ dataSource = HistoryDataSourceImpl(service)
+ }
+
+ @Test
+ fun getHistoryData() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery {
+ service.getAllTransactionHistory(
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ } returns mockResponse
+ val actualResult = dataSource.getHistoryData(5000, null, null, null)
+ coVerify { service.getAllTransactionHistory(any(), any(), any(), any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/notification/NotificationItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/notification/NotificationItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..d5045ff
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/notification/NotificationItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,40 @@
+package com.kom.skyfly.data.datasource.notification
+
+import com.kom.skyfly.data.source.network.model.notification.NotificationResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class NotificationItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var service: SkyFlyApiService
+ private lateinit var dataSource: NotificationDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ dataSource = NotificationDataSourceImpl(service)
+ }
+
+ @Test
+ fun getAllNotification() {
+ runTest {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.getAllNotification(any()) } returns mockResponse
+ val actualResult = dataSource.getAllNotification()
+ coVerify { service.getAllNotification(any()) }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..91e9210
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/searchhistory/SearchHistoryItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,95 @@
+package com.kom.skyfly.data.datasource.searchhistory
+
+import app.cash.turbine.test
+import com.kom.skyfly.data.source.local.database.dao.SearchHistoryDao
+import com.kom.skyfly.data.source.local.database.entity.SearchHistoryEntity
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class SearchHistoryItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var searchHistoryDao: SearchHistoryDao
+ private lateinit var searchHistoryDataSource: SearchHistoryDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ searchHistoryDataSource = SearchHistoryDataSourceImpl(searchHistoryDao)
+ }
+
+ @Test
+ fun getAllSearchHistory() {
+ val entity1 = mockk()
+ val entity2 = mockk()
+
+ val listEntity = listOf(entity1, entity2)
+ val mockFlow =
+ flow {
+ emit(listEntity)
+ }
+ every { searchHistoryDao.getAllSearchHistory() } returns mockFlow
+ runTest {
+ searchHistoryDataSource.getAllSearchHistory().test {
+ val result = awaitItem()
+ assertEquals(listEntity.size, result.size)
+ awaitComplete()
+ }
+ }
+ }
+
+ @Test
+ fun insertSearchHistory() {
+ runTest {
+ val mockEntity = mockk()
+ coEvery { searchHistoryDao.insertSearchHistory(any()) } returns 1
+ val result = searchHistoryDataSource.insertSearchHistory(mockEntity)
+ coVerify { searchHistoryDao.insertSearchHistory(any()) }
+ assertEquals(1, result)
+ }
+ }
+
+ @Test
+ fun updateSearchHistory() {
+ runTest {
+ val mockEntity = mockk()
+ coEvery { searchHistoryDao.updateSearchHistory(any()) } returns 1
+ val result = searchHistoryDataSource.updateSearchHistory(mockEntity)
+ coVerify { searchHistoryDao.updateSearchHistory(any()) }
+ assertEquals(1, result)
+ }
+ }
+
+ @Test
+ fun deleteSearchHistory() {
+ runTest {
+ val mockEntity = mockk()
+ coEvery { searchHistoryDao.deleteSearchHistory(any()) } returns 1
+ val result = searchHistoryDataSource.deleteSearchHistory(mockEntity)
+ coVerify { searchHistoryDao.deleteSearchHistory(any()) }
+ assertEquals(1, result)
+ }
+ }
+
+ @Test
+ fun deleteAllSearchHistory() {
+ runTest {
+ coEvery { searchHistoryDao.deleteAll() } returns Unit
+ val result = searchHistoryDataSource.deleteAllSearchHistory()
+ coVerify { searchHistoryDao.deleteAll() }
+ assertEquals(Unit, result)
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/transaction/TransactionItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/transaction/TransactionItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..44ee77b
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/transaction/TransactionItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,94 @@
+package com.kom.skyfly.data.datasource.transaction
+
+import com.kom.skyfly.data.source.network.model.transaction.request.Bookers
+import com.kom.skyfly.data.source.network.model.transaction.request.Passenger
+import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
+import com.kom.skyfly.data.source.network.model.transaction.response.TransactionResponse
+import com.kom.skyfly.data.source.network.services.SkyFlyApiService
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class TransactionItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var service: SkyFlyApiService
+ private lateinit var dataSource: TransactionDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ dataSource = TransactionDataSourceImpl(service)
+ }
+
+ @Test
+ fun createTransaction() {
+ runTest {
+ val flightId = "example_flight_id"
+ val adultCount = 1
+ val childCount = 0
+ val babyCount = 1
+ val mockTransactionRequest =
+ TransactionRequest(
+ orderer = Bookers("exampleFullName", "ExampleFamilyName", "62737478454", "example@gmail.com"),
+ passengers =
+ listOf(
+ Passenger(
+ type = "Adult",
+ title = "Mr",
+ fullName = "John Doe",
+ dob = "1985-05-20",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "ABC123456",
+ issuingCountry = "USA",
+ price = 100,
+ quantity = 1,
+ seatId = "A1",
+ ),
+ Passenger(
+ type = "Child",
+ title = "Miss",
+ fullName = "Jane Doe",
+ dob = "2010-08-15",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "DEF789012",
+ issuingCountry = "USA",
+ price = 50,
+ quantity = 1,
+ seatId = "B2",
+ ),
+ ),
+ )
+ val mockResponse = mockk(relaxed = true)
+ coEvery { service.createTransaction(flightId, adultCount, childCount, babyCount, mockTransactionRequest) } returns mockResponse
+ val actualResult = dataSource.createTransaction(flightId, adultCount, childCount, babyCount, mockTransactionRequest)
+ coVerify {
+ service.createTransaction(
+ flightId,
+ adultCount,
+ childCount,
+ babyCount,
+ mockTransactionRequest,
+ )
+ }
+ assertEquals(mockResponse, actualResult)
+ }
+ }
+
+ @Test
+ fun getTransactionById() {
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/datasource/userpreference/UserPrefItemsPaymentStatusModelSourceImplTest.kt b/app/src/test/java/com/kom/skyfly/data/datasource/userpreference/UserPrefItemsPaymentStatusModelSourceImplTest.kt
new file mode 100644
index 0000000..990daec
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/datasource/userpreference/UserPrefItemsPaymentStatusModelSourceImplTest.kt
@@ -0,0 +1,62 @@
+package com.kom.skyfly.data.datasource.userpreference
+
+import com.kom.skyfly.data.source.local.pref.UserPreference
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.verify
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+class UserPrefItemsPaymentStatusModelSourceImplTest {
+ @MockK
+ lateinit var userPreference: UserPreference
+
+ private lateinit var userPrefDataSource: UserPrefDataSource
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ userPrefDataSource = UserPrefDataSourceImpl(userPreference)
+ }
+
+ @Test
+ fun isOnBoardingShow() {
+ every { userPreference.isOnBoardingShow() } returns true
+ val result = userPrefDataSource.isOnBoardingShow()
+ verify { userPreference.isOnBoardingShow() }
+ assertEquals(true, result)
+ }
+
+ @Test
+ fun setOnBoardingShow() {
+ val isShow = true
+ every { userPreference.setOnBoardingShow(isShow) } returns Unit
+ userPrefDataSource.setOnBoardingShow(isShow)
+ verify { userPreference.setOnBoardingShow(isShow) }
+ }
+
+ @Test
+ fun saveUserToken() {
+ every { userPreference.saveUserToken(any()) } returns Unit
+ userPrefDataSource.saveUserToken("eyjieijefojgefo")
+ verify { userPreference.saveUserToken(any()) }
+ }
+
+ @Test
+ fun getUserToken() {
+ val expectedToken = "eyjjfelhgbeneofe"
+ every { userPreference.getUserToken() } returns expectedToken
+ val result = userPrefDataSource.getUserToken()
+ verify { userPreference.getUserToken() }
+ assertEquals(expectedToken, result)
+ }
+
+ @Test
+ fun clearAll() {
+ every { userPreference.clearAll() } returns Unit
+ userPrefDataSource.clearAll()
+ verify { userPreference.clearAll() }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/repository/auth/AuthRepositoryImplTest.kt b/app/src/test/java/com/kom/skyfly/data/repository/auth/AuthRepositoryImplTest.kt
new file mode 100644
index 0000000..109363a
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/repository/auth/AuthRepositoryImplTest.kt
@@ -0,0 +1,357 @@
+package com.kom.skyfly.data.repository.auth
+
+import app.cash.turbine.test
+import com.kom.skyfly.data.datasource.auth.AuthDataSource
+import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordResponse
+import com.kom.skyfly.data.source.network.model.login.LoginResponse
+import com.kom.skyfly.data.source.network.model.register.RegisterResponse
+import com.kom.skyfly.data.source.network.model.resendotp.ResendOtpResponse
+import com.kom.skyfly.data.source.network.model.verifyaccount.VerifyAccountResponse
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.runTest
+import okio.IOException
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+
+class AuthRepositoryImplTest {
+ @MockK
+ lateinit var dataSource: AuthDataSource
+
+ private lateinit var repository: AuthRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ repository = AuthRepositoryImpl(dataSource)
+ }
+
+ @Test
+ fun doLogin_success() {
+ val mockResponse = mockk(relaxed = true)
+
+ coEvery { dataSource.doLogin(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.doLogin("komang@gmail.com", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.doLogin(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doLogin_loading() {
+ val mockResponse = mockk(relaxed = true)
+
+ coEvery { dataSource.doLogin(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.doLogin("komang@gmail.com", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { dataSource.doLogin(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doLogin_error() {
+ val mockResponse = mockk(relaxed = true)
+
+ coEvery { dataSource.doLogin(any(), any()) } throws IOException("loginError")
+
+ runTest {
+ repository.doLogin("komang@gmail.com", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.doLogin(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doRegister_success() {
+ val mockResponse = mockk(relaxed = true)
+
+ coEvery { dataSource.doLogin(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.doLogin("komang@gmail.com", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.doLogin(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doRegister_loading() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.doRegister(any(), any(), any(), any()) } returns mockResponse
+ runTest {
+ repository.doRegister("Komangyuda", "komang@gmail.com", "6277487488", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { dataSource.doRegister(any(), any(), any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doRegister_error() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery {
+ dataSource.doRegister(
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ } throws IOException("register error")
+ runTest {
+ repository.doRegister("Komangyuda", "komang@gmail.com", "6277487488", "password").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.doRegister(any(), any(), any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doVerifyAccount_success() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.doVerifyAccount(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.doVerifyAccount("eyjigoejogenmig", "675894").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.doVerifyAccount(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doVerifyAccount_error() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.doVerifyAccount(any(), any()) } throws IOException("verify failed")
+
+ runTest {
+ repository.doVerifyAccount("eyjigoejogenmig", "675894").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.doVerifyAccount(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun doVerifyAccount_loading() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.doVerifyAccount(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.doVerifyAccount("eyjigoejogenmig", "675894").map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { dataSource.doVerifyAccount(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun forgetPassword_success() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.forgetPassword(any()) } returns mockResponse
+
+ runTest {
+ repository.forgetPassword("komang@gmail.com").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.forgetPassword(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun forgetPassword_error() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.forgetPassword(any()) } throws IOException("error")
+
+ runTest {
+ repository.forgetPassword("komang@gmail.com").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.forgetPassword(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun forgetPassword_loading() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.forgetPassword(any()) } returns mockResponse
+
+ runTest {
+ repository.forgetPassword("komang@gmail.com").map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { dataSource.forgetPassword(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun resendOtpRequest_success() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.resendOtpRequest(any()) } returns mockResponse
+
+ runTest {
+ repository.resendOtpRequest("eymkgejfeognnkfm").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.resendOtpRequest(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun resendOtpRequest_error() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.resendOtpRequest(any()) } throws IOException("error")
+
+ runTest {
+ repository.resendOtpRequest("eymkgejfeognnkfm").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.resendOtpRequest(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun resendOtpSmsRequest_success() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.resendOtpSmsRequest(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.resendOtpSmsRequest("eymkgejfeognnkfm", "6263774787").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockResponse, actualData.payload)
+ coVerify { dataSource.resendOtpSmsRequest(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun resendOtpSmsRequest_error() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.resendOtpSmsRequest(any(), any()) } throws IOException("error")
+
+ runTest {
+ repository.resendOtpSmsRequest("eymkgejfeognnkfm", "6263774787").map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { dataSource.resendOtpSmsRequest(any(), any()) }
+ }
+ }
+ }
+
+ @Test
+ fun resendOtpSmsRequest_loading() {
+ val mockResponse = mockk(relaxed = true)
+ coEvery { dataSource.resendOtpSmsRequest(any(), any()) } returns mockResponse
+
+ runTest {
+ repository.resendOtpSmsRequest("eymkgejfeognnkfm", "6263774787").map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { dataSource.resendOtpSmsRequest(any(), any()) }
+ }
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/repository/notification/NotificationRepositoryImplTest.kt b/app/src/test/java/com/kom/skyfly/data/repository/notification/NotificationRepositoryImplTest.kt
new file mode 100644
index 0000000..7f1738b
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/repository/notification/NotificationRepositoryImplTest.kt
@@ -0,0 +1,108 @@
+package com.kom.skyfly.data.repository.notification
+
+import app.cash.turbine.test
+import com.kom.skyfly.data.datasource.notification.NotificationDataSource
+import com.kom.skyfly.data.source.network.model.notification.ItemNotificationResponse
+import com.kom.skyfly.data.source.network.model.notification.NotificationResponse
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class NotificationRepositoryImplTest {
+ @MockK
+ lateinit var datasource: NotificationDataSource
+
+ private lateinit var repository: NotificationRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ repository = NotificationRepositoryImpl(datasource)
+ }
+
+ @Test
+ fun getAllNotification_success() {
+ val notification1 =
+ ItemNotificationResponse(
+ date = "2024-06-25T10:30:00Z",
+ id = "1",
+ notificationsContent = "This is notification content 1.",
+ notificationsTitle = "Notification Title 1",
+ type = "info",
+ )
+
+ val notification2 =
+ ItemNotificationResponse(
+ date = "2024-06-24T15:45:00Z",
+ id = "2",
+ notificationsContent = "This is notification content 2.",
+ notificationsTitle = "Notification Title 2",
+ type = "alert",
+ )
+ val mockListNotification = listOf(notification1, notification2)
+ val mockResponse = mockk()
+ every { mockResponse.data } returns mockListNotification
+ runTest {
+ coEvery { datasource.getAllNotification() } returns mockResponse
+ repository.getAllNotification().map {
+ delay(100)
+ it
+ }.test {
+ delay(1210)
+ val data = expectMostRecentItem()
+ assertTrue(data is ResultWrapper.Success)
+ coVerify { datasource.getAllNotification() }
+ }
+ }
+ }
+
+ @Test
+ fun getAllNotification_loading() {
+ val notification1 =
+ ItemNotificationResponse(
+ date = "2024-06-25T10:30:00Z",
+ id = "1",
+ notificationsContent = "This is notification content 1.",
+ notificationsTitle = "Notification Title 1",
+ type = "info",
+ )
+
+ val notification2 =
+ ItemNotificationResponse(
+ date = "2024-06-24T15:45:00Z",
+ id = "2",
+ notificationsContent = "This is notification content 2.",
+ notificationsTitle = "Notification Title 2",
+ type = "alert",
+ )
+ val mockListNotification = listOf(notification1, notification2)
+ val mockResponse = mockk()
+ every { mockResponse.data } returns mockListNotification
+ runTest {
+ coEvery { datasource.getAllNotification() } returns mockResponse
+ repository.getAllNotification().map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val data = expectMostRecentItem()
+ assertTrue(data is ResultWrapper.Loading)
+ coVerify { datasource.getAllNotification() }
+ }
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepositoryImplTest.kt b/app/src/test/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepositoryImplTest.kt
new file mode 100644
index 0000000..0bb54dd
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/repository/searchhistory/SearchHistoryRepositoryImplTest.kt
@@ -0,0 +1,240 @@
+package com.kom.skyfly.data.repository.searchhistory
+
+import app.cash.turbine.test
+import com.kom.skyfly.data.datasource.searchhistory.SearchHistoryDataSource
+import com.kom.skyfly.data.model.searchhistory.SearchHistory
+import com.kom.skyfly.data.source.local.database.entity.SearchHistoryEntity
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class SearchHistoryRepositoryImplTest {
+ @MockK
+ lateinit var datasource: SearchHistoryDataSource
+ private lateinit var repository: SearchHistoryRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ repository = SearchHistoryRepositoryImpl(datasource)
+ }
+
+ @Test
+ fun createSearchHistory_success() {
+ val mockProduct = mockk(relaxed = true)
+ every { mockProduct.id } returns 1
+ coEvery { datasource.insertSearchHistory(any()) } returns 1
+ runTest {
+ repository.createSearchHistory("feoihfieo", "JKT-DPS")
+ .map {
+ delay(100)
+ it
+ }.test {
+ delay(2201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(true, actualData.payload)
+ coVerify { datasource.insertSearchHistory(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun createSearchHistory_loading() {
+ val mockProduct = mockk(relaxed = true)
+ every { mockProduct.id } returns 1
+ coEvery { datasource.insertSearchHistory(any()) } returns 1
+ runTest {
+ repository.createSearchHistory("feoihfieo", "JKT-DPS")
+ .map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ coVerify { datasource.insertSearchHistory(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun createSearchHistory_error() {
+ val mockSearchHistory = mockk(relaxed = true)
+ every { mockSearchHistory.id } returns 1
+ coEvery { datasource.insertSearchHistory(any()) } throws IllegalStateException("error")
+ runTest {
+ repository.createSearchHistory("feoihfieo", "JKT-DPS")
+ .map {
+ delay(100)
+ it
+ }.test {
+ delay(2201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ coVerify { datasource.insertSearchHistory(any()) }
+ }
+ }
+ }
+
+ @Test
+ fun deleteSearchHistory() {
+ val mockSearch =
+ SearchHistory(
+ id = 1,
+ userId = "geojegegf",
+ searchHistory = "JKT-DPS",
+ )
+ coEvery { datasource.deleteSearchHistory(any()) } returns 1
+ runTest {
+ val result =
+ repository.deleteSearchHistory(mockSearch).map {
+ delay(100)
+ it
+ }.test {
+ delay(210)
+ val actualResult = expectMostRecentItem()
+ assertTrue(actualResult is ResultWrapper.Success)
+ }
+ coVerify { datasource.deleteSearchHistory(any()) }
+ }
+ }
+
+ @Test
+ fun deleteAllSearchHistory() {
+ coEvery { datasource.deleteAllSearchHistory() } returns Unit
+ runTest {
+ val result =
+ repository.deleteAllSearchHistory().map {
+ delay(100)
+ it
+ }.test {
+ delay(210)
+ val actualResult = expectMostRecentItem()
+ assertTrue(actualResult is ResultWrapper.Success)
+ }
+ coVerify { datasource.deleteAllSearchHistory() }
+ }
+ }
+
+ @Test
+ fun getUserSearchHistory_success() {
+ val entity1 =
+ SearchHistoryEntity(
+ id = 1,
+ userId = "ejiegef",
+ searchHistory = "JKT",
+ )
+ val entity2 =
+ SearchHistoryEntity(
+ id = 2,
+ userId = "ejiegef",
+ searchHistory = "DPS",
+ )
+ val mockList = listOf(entity1, entity2)
+ val mockFlow =
+ flow {
+ emit(mockList)
+ }
+ every { datasource.getAllSearchHistory() } returns mockFlow
+ runTest {
+ repository.getUserSearchHistory().map {
+ delay(100)
+ it
+ }.test {
+ delay(2201)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Success)
+ assertEquals(mockList.size, actualData.payload?.size)
+ verify { datasource.getAllSearchHistory() }
+ }
+ }
+ }
+
+ @Test
+ fun getUserSearchHistory_error() {
+ every { datasource.getAllSearchHistory() } returns
+ flow {
+ throw IllegalStateException("error")
+ }
+ runTest {
+ repository.getUserSearchHistory().map {
+ delay(100)
+ it
+ }.test {
+ delay(2210)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Error)
+ verify { datasource.getAllSearchHistory() }
+ }
+ }
+ }
+
+// @Test
+// fun getUserSearchHistory_empty() {
+// val mockList = listOf()
+// val mockFlow = flow { emit(mockList) }
+// every { datasource.getAllSearchHistory() } returns mockFlow
+//
+// runTest {
+// repository.getUserSearchHistory().map {
+// delay(100)
+// it
+// }.test {
+// delay(210)
+// val actualResult = expectMostRecentItem()
+// assertTrue(actualResult is ResultWrapper.Empty)
+// verify { datasource.getAllSearchHistory() }
+// }
+// }
+// }
+
+ @Test
+ fun getUserSearchHistory_loading() {
+ val entity1 =
+ SearchHistoryEntity(
+ id = 1,
+ userId = "ejiegef",
+ searchHistory = "JKT",
+ )
+ val entity2 =
+ SearchHistoryEntity(
+ id = 2,
+ userId = "ejiegef",
+ searchHistory = "DPS",
+ )
+ val mockList = listOf(entity1, entity2)
+ val mockFlow =
+ flow {
+ emit(mockList)
+ }
+ every { datasource.getAllSearchHistory() } returns mockFlow
+ runTest {
+ repository.getUserSearchHistory().map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualData = expectMostRecentItem()
+ assertTrue(actualData is ResultWrapper.Loading)
+ verify { datasource.getAllSearchHistory() }
+ }
+ }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/repository/transaction/TransactionRepositoryImplTest.kt b/app/src/test/java/com/kom/skyfly/data/repository/transaction/TransactionRepositoryImplTest.kt
new file mode 100644
index 0000000..6d08319
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/repository/transaction/TransactionRepositoryImplTest.kt
@@ -0,0 +1,313 @@
+package com.kom.skyfly.data.repository.transaction
+
+import app.cash.turbine.test
+import com.kom.skyfly.data.datasource.transaction.TransactionDataSource
+import com.kom.skyfly.data.source.network.model.transaction.request.Bookers
+import com.kom.skyfly.data.source.network.model.transaction.request.Passenger
+import com.kom.skyfly.data.source.network.model.transaction.request.TransactionRequest
+import com.kom.skyfly.data.source.network.model.transaction.response.TransactionResponse
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.impl.annotations.MockK
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.runTest
+import okio.IOException
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+@ExperimentalCoroutinesApi
+class TransactionRepositoryImplTest {
+ @MockK
+ lateinit var dataSource: TransactionDataSource
+ private lateinit var repository: TransactionRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ repository = TransactionRepositoryImpl(dataSource)
+ }
+
+ @Test
+ fun createTransaction_success() =
+ runTest {
+ val flightId = "flight123"
+ val adult = 2
+ val child = 1
+ val baby = 0
+ val transactionRequest =
+ TransactionRequest(
+ orderer =
+ Bookers(
+ "exampleFullName",
+ "ExampleFamilyName",
+ "62737478454",
+ "example@gmail.com",
+ ),
+ passengers =
+ listOf(
+ Passenger(
+ type = "Adult",
+ title = "Mr",
+ fullName = "John Doe",
+ dob = "1985-05-20",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "ABC123456",
+ issuingCountry = "USA",
+ price = 100,
+ quantity = 1,
+ seatId = "A1",
+ ),
+ Passenger(
+ type = "Child",
+ title = "Miss",
+ fullName = "Jane Doe",
+ dob = "2010-08-15",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "DEF789012",
+ issuingCountry = "USA",
+ price = 50,
+ quantity = 1,
+ seatId = "B2",
+ ),
+ ),
+ )
+
+ val mockResponse =
+ TransactionResponse(
+ data = null,
+ message = "Transaction created successfully",
+ redirectUrl = "/transaction/success",
+ status = true,
+ token = "token123",
+ transactionId = "transaction456",
+ )
+
+ coEvery {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ } returns mockResponse
+
+ // Execute the repository method
+ val flow =
+ repository.createTransaction(flightId, adult, child, baby, transactionRequest).map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualResult = expectMostRecentItem()
+ assertTrue(actualResult is ResultWrapper.Success)
+ }
+
+ coVerify {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ }
+ }
+
+ @Test
+ fun createTransaction_loading() =
+ runTest {
+ val flightId = "flight123"
+ val adult = 2
+ val child = 1
+ val baby = 0
+ val transactionRequest =
+ TransactionRequest(
+ orderer =
+ Bookers(
+ "exampleFullName",
+ "ExampleFamilyName",
+ "62737478454",
+ "example@gmail.com",
+ ),
+ passengers =
+ listOf(
+ Passenger(
+ type = "Adult",
+ title = "Mr",
+ fullName = "John Doe",
+ dob = "1985-05-20",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "ABC123456",
+ issuingCountry = "USA",
+ price = 100,
+ quantity = 1,
+ seatId = "A1",
+ ),
+ Passenger(
+ type = "Child",
+ title = "Miss",
+ fullName = "Jane Doe",
+ dob = "2010-08-15",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "DEF789012",
+ issuingCountry = "USA",
+ price = 50,
+ quantity = 1,
+ seatId = "B2",
+ ),
+ ),
+ )
+
+ val mockResponse =
+ TransactionResponse(
+ data = null,
+ message = "Transaction created successfully",
+ redirectUrl = "/transaction/success",
+ status = true,
+ token = "token123",
+ transactionId = "transaction456",
+ )
+
+ coEvery {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ } returns mockResponse
+
+ val flow =
+ repository.createTransaction(flightId, adult, child, baby, transactionRequest).map {
+ delay(100)
+ it
+ }.test {
+ delay(111)
+ val actualResult = expectMostRecentItem()
+ assertTrue(actualResult is ResultWrapper.Loading)
+ }
+
+ coVerify {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ }
+ }
+
+ @Test
+ fun createTransaction_error() =
+ runTest {
+ val flightId = "flight123"
+ val adult = 2
+ val child = 1
+ val baby = 0
+ val transactionRequest =
+ TransactionRequest(
+ orderer =
+ Bookers(
+ "exampleFullName",
+ "ExampleFamilyName",
+ "62737478454",
+ "example@gmail.com",
+ ),
+ passengers =
+ listOf(
+ Passenger(
+ type = "Adult",
+ title = "Mr",
+ fullName = "John Doe",
+ dob = "1985-05-20",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "ABC123456",
+ issuingCountry = "USA",
+ price = 100,
+ quantity = 1,
+ seatId = "A1",
+ ),
+ Passenger(
+ type = "Child",
+ title = "Miss",
+ fullName = "Jane Doe",
+ dob = "2010-08-15",
+ validityPeriod = "2024-06-30",
+ familyName = "Doe",
+ citizenship = "US",
+ passport = "DEF789012",
+ issuingCountry = "USA",
+ price = 50,
+ quantity = 1,
+ seatId = "B2",
+ ),
+ ),
+ )
+
+ val mockResponse =
+ TransactionResponse(
+ data = null,
+ message = "Transaction created successfully",
+ redirectUrl = "/transaction/success",
+ status = true,
+ token = "token123",
+ transactionId = "transaction456",
+ )
+
+ coEvery {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ } throws IOException("error")
+
+ val flow =
+ repository.createTransaction(flightId, adult, child, baby, transactionRequest).map {
+ delay(100)
+ it
+ }.test {
+ delay(201)
+ val actualResult = expectMostRecentItem()
+ assertTrue(actualResult is ResultWrapper.Error)
+ }
+
+ coVerify {
+ dataSource.createTransaction(
+ flightId,
+ adult,
+ child,
+ baby,
+ transactionRequest,
+ )
+ }
+ }
+
+ @Test
+ fun getTransactionById() {
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/data/repository/userpref/UserPrefRepositoryImplTest.kt b/app/src/test/java/com/kom/skyfly/data/repository/userpref/UserPrefRepositoryImplTest.kt
new file mode 100644
index 0000000..a67e7a2
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/data/repository/userpref/UserPrefRepositoryImplTest.kt
@@ -0,0 +1,66 @@
+package com.kom.skyfly.data.repository.userpref
+
+import com.kom.skyfly.data.datasource.userpreference.UserPrefDataSource
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.verify
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class UserPrefRepositoryImplTest {
+ @MockK
+ lateinit var dataSource: UserPrefDataSource
+ private lateinit var userPrefRepository: UserPrefRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ userPrefRepository = UserPrefRepositoryImpl(dataSource)
+ }
+
+ @Test
+ fun isOnBoardingShow() {
+ every { dataSource.isOnBoardingShow() } returns true
+
+ val actualResult = userPrefRepository.isOnBoardingShow()
+
+ assertTrue(actualResult)
+ verify { dataSource.isOnBoardingShow() }
+ }
+
+ @Test
+ fun setOnBoardingShow() {
+ every { dataSource.setOnBoardingShow(any()) } returns Unit
+ userPrefRepository.setOnBoardingShow(true)
+ verify { dataSource.setOnBoardingShow(true) }
+ }
+
+ @Test
+ fun saveUserToken() {
+ every { dataSource.saveUserToken(any()) } returns Unit
+ userPrefRepository.saveUserToken("eysjgjoejgbjfohg")
+ verify { dataSource.saveUserToken(any()) }
+ }
+
+ @Test
+ fun getUserToken() {
+ val token = "eyjgepemfkgef"
+ every { dataSource.getUserToken() } returns token
+ val actualToken = userPrefRepository.getUserToken()
+ assertEquals(token, actualToken)
+ verify { dataSource.getUserToken() }
+ }
+
+ @Test
+ fun clearAll() {
+ every { dataSource.clearAll() } returns Unit
+ userPrefRepository.clearAll()
+ verify { dataSource.clearAll() }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/account/AccountViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/account/AccountViewModelTest.kt
new file mode 100644
index 0000000..1553e74
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/account/AccountViewModelTest.kt
@@ -0,0 +1,101 @@
+package com.kom.skyfly.presentation.account
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.model.auth.Auth
+import com.kom.skyfly.data.model.profiles.Profiles
+import com.kom.skyfly.data.repository.auth.AuthRepository
+import com.kom.skyfly.data.repository.profiles.ProfileRepository
+import com.kom.skyfly.data.repository.userpref.UserPrefRepository
+import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordResponse
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.tools.getOrAwaitValue
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class AccountViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var authRepository: AuthRepository
+
+ @MockK
+ lateinit var userPrefRepository: UserPrefRepository
+
+ @MockK
+ lateinit var profileRepository: ProfileRepository
+
+ private lateinit var viewModel: AccountViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ AccountViewModel(userPrefRepository, profileRepository, authRepository),
+ )
+ }
+
+ @Test
+ fun doLogout() {
+ val token = "exampleToken"
+ every { userPrefRepository.saveUserToken(token) } returns Unit
+ viewModel.doLogout(token)
+ verify { userPrefRepository.saveUserToken(token) }
+ }
+
+ @Test
+ fun getProfile() {
+ val mockkResponse = mockk(relaxed = true)
+ every { profileRepository.getProfile() } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ val result = viewModel.getProfile().getOrAwaitValue()
+ assertEquals(mockkResponse, result.payload)
+ verify { profileRepository.getProfile() }
+ }
+
+ @Test
+ fun forgetPassword() {
+ val mockkResponse = mockk(relaxed = true)
+ every { authRepository.forgetPassword(any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ viewModel.forgetPassword("example@gmail.com")
+ verify { authRepository.forgetPassword(any()) }
+ }
+
+ @Test
+ fun isUserLoggedIn() {
+ val mockkResponse = mockk()
+ every { authRepository.isUserLoggedIn() } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ viewModel.isUserLoggedIn()
+ verify { authRepository.isUserLoggedIn() }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfileTest.kt b/app/src/test/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfileTest.kt
new file mode 100644
index 0000000..90c9173
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/account/editprofile/SharedViewModelEditProfileTest.kt
@@ -0,0 +1,59 @@
+package com.kom.skyfly.presentation.account.editprofile
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.repository.profiles.ProfileRepository
+import com.kom.skyfly.data.source.network.model.userprofile.updateprofile.UpdateProfileResponse
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class SharedViewModelEditProfileTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var profileRepository: ProfileRepository
+
+ private lateinit var viewModel: SharedViewModelEditProfile
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ SharedViewModelEditProfile(profileRepository),
+ )
+ }
+
+ @Test
+ fun updateProfile() {
+ val mockkResponse = mockk(relaxed = true)
+ every { profileRepository.updateProfile(any(), any(), any(), any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ viewModel.updateProfile("example name", "887387384", "examplepass", "examplepass")
+ verify { profileRepository.updateProfile(any(), any(), any(), any()) }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataViewModelTest.kt
new file mode 100644
index 0000000..2116275
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/checkout/bookersbiodata/BookersBiodataViewModelTest.kt
@@ -0,0 +1,61 @@
+package com.kom.skyfly.presentation.checkout.bookersbiodata
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.model.profiles.Profiles
+import com.kom.skyfly.data.repository.profiles.ProfileRepository
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.tools.getOrAwaitValue
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class BookersBiodataViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var profileRepository: ProfileRepository
+
+ private lateinit var viewModel: BookersBiodataViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ BookersBiodataViewModel(profileRepository),
+ )
+ }
+
+ @Test
+ fun getProfile() {
+ val mockkResponse = mockk(relaxed = true)
+ every { profileRepository.getProfile() } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ val result = viewModel.getProfile().getOrAwaitValue()
+ assertEquals(mockkResponse, result.payload)
+ verify { profileRepository.getProfile() }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketViewModelTest.kt
new file mode 100644
index 0000000..ef1ce73
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/checkout/checkoutticket/CheckoutTicketViewModelTest.kt
@@ -0,0 +1,59 @@
+package com.kom.skyfly.presentation.checkout.checkoutticket
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
+import com.kom.skyfly.data.repository.transaction.TransactionRepository
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.tools.getOrAwaitValue
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class CheckoutTicketViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var transactionRepository: TransactionRepository
+
+ private lateinit var viewModel: CheckoutTicketViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ CheckoutTicketViewModel(transactionRepository),
+ )
+ }
+
+ @Test
+ fun getTransactionById() {
+ val mockkResponse = mockk(relaxed = true)
+ every { transactionRepository.getTransactionById(any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ val result = viewModel.getTransactionById("1").getOrAwaitValue()
+ assertEquals(mockkResponse, result.payload)
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailViewModelTest.kt
new file mode 100644
index 0000000..31ff80a
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/checkout/flightdetail/FlightDetailViewModelTest.kt
@@ -0,0 +1,59 @@
+package com.kom.skyfly.presentation.checkout.flightdetail
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.model.transaction.detail.TransactionDetailResponses
+import com.kom.skyfly.data.repository.transaction.TransactionRepository
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.tools.getOrAwaitValue
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class FlightDetailViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var transactionRepository: TransactionRepository
+
+ private lateinit var viewModel: FlightDetailViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ FlightDetailViewModel(transactionRepository),
+ )
+ }
+
+ @Test
+ fun getTransactionById() {
+ val mockkResponse = mockk(relaxed = true)
+ every { transactionRepository.getTransactionById(any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ val result = viewModel.getTransactionById("1").getOrAwaitValue()
+ assertEquals(mockkResponse, result.payload)
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/forgetpassword/ForgetPasswordViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/forgetpassword/ForgetPasswordViewModelTest.kt
new file mode 100644
index 0000000..5b47619
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/forgetpassword/ForgetPasswordViewModelTest.kt
@@ -0,0 +1,59 @@
+package com.kom.skyfly.presentation.forgetpassword
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.repository.auth.AuthRepository
+import com.kom.skyfly.data.source.network.model.forgetpassword.ForgetPasswordResponse
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class ForgetPasswordViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var authRepository: AuthRepository
+
+ private lateinit var viewModel: ForgetPasswordViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ ForgetPasswordViewModel(authRepository),
+ )
+ }
+
+ @Test
+ fun forgetPassword() {
+ val mockkResponse = mockk(relaxed = true)
+ every { authRepository.forgetPassword(any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ viewModel.forgetPassword("example@gmail.com")
+ verify { authRepository.forgetPassword(any()) }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/history/HistoryViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/history/HistoryViewModelTest.kt
new file mode 100644
index 0000000..73582ee
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/history/HistoryViewModelTest.kt
@@ -0,0 +1,63 @@
+package com.kom.skyfly.presentation.history
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.model.history.new.HistoryDomain
+import com.kom.skyfly.data.repository.history.HistoryRepository
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.tools.getOrAwaitValue
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class HistoryViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var historyRepository: HistoryRepository
+
+ private lateinit var viewModel: HistoryViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ HistoryViewModel(
+ historyRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun getHistoryData() {
+ val mockkResponse = mockk(relaxed = true)
+ every { historyRepository.getHistoryData(any(), any(), any(), any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ val result = viewModel.getHistoryData(null, null, null, null).getOrAwaitValue()
+ assertEquals(mockkResponse, result.payload)
+ verify { historyRepository.getHistoryData(any(), any(), any(), any()) }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/login/LoginViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/login/LoginViewModelTest.kt
new file mode 100644
index 0000000..a4bd83f
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/login/LoginViewModelTest.kt
@@ -0,0 +1,70 @@
+package com.kom.skyfly.presentation.login
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.repository.auth.AuthRepository
+import com.kom.skyfly.data.repository.userpref.UserPrefRepository
+import com.kom.skyfly.data.source.network.model.login.LoginResponse
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class LoginViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var authRepository: AuthRepository
+
+ @MockK
+ lateinit var userPrefRepository: UserPrefRepository
+ private lateinit var viewModel: LoginViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ LoginViewModel(authRepository, userPrefRepository),
+ )
+ }
+
+ @Test
+ fun doLogin() {
+ val mockkResponse = mockk(relaxed = true)
+ every { authRepository.doLogin(any(), any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockkResponse))
+ }
+ viewModel.doLogin("example@gmail.com", "password")
+ verify { authRepository.doLogin(any(), any()) }
+ }
+
+ @Test
+ fun saveUserToken() {
+ val token = "exampleToken"
+ every { userPrefRepository.saveUserToken(token) } returns Unit
+ viewModel.saveUserToken(token)
+ verify { userPrefRepository.saveUserToken(token) }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/presentation/register/RegisterViewModelTest.kt b/app/src/test/java/com/kom/skyfly/presentation/register/RegisterViewModelTest.kt
new file mode 100644
index 0000000..b0294e1
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/presentation/register/RegisterViewModelTest.kt
@@ -0,0 +1,59 @@
+package com.kom.skyfly.presentation.register
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.kom.skyfly.data.repository.auth.AuthRepository
+import com.kom.skyfly.data.source.network.model.register.RegisterResponse
+import com.kom.skyfly.tools.MainCoroutineRule
+import com.kom.skyfly.utils.ResultWrapper
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
+import io.mockk.spyk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+/**
+ * Written by Komang Yuda Saputra
+ * Github : https://github.com/YudaSaputraa
+ */
+class RegisterViewModelTest {
+ @get:Rule
+ val testRule: TestRule = InstantTaskExecutorRule()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @get:Rule
+ val coroutineRule: TestRule = MainCoroutineRule(UnconfinedTestDispatcher())
+
+ @MockK
+ lateinit var authRepository: AuthRepository
+
+ private lateinit var viewModel: RegisterViewModel
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ viewModel =
+ spyk(
+ RegisterViewModel(authRepository),
+ )
+ }
+
+ @Test
+ fun doRegister() {
+ val mockResponse = mockk(relaxed = true)
+ every { authRepository.doRegister(any(), any(), any(), any()) } returns
+ flow {
+ emit(ResultWrapper.Success(mockResponse))
+ }
+ viewModel.doRegister("exampleFullName", "example@gmail.com", "39839734", "examplePass")
+ verify { authRepository.doRegister(any(), any(), any(), any()) }
+ }
+}
diff --git a/app/src/test/java/com/kom/skyfly/tools/LiveDataTestUtils.kt b/app/src/test/java/com/kom/skyfly/tools/LiveDataTestUtils.kt
new file mode 100644
index 0000000..6409e4b
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/tools/LiveDataTestUtils.kt
@@ -0,0 +1,53 @@
+package com.kom.skyfly.tools
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+/**
+ * Helper method for testing LiveData objects, from
+ * https://github.com/googlesamples/android-architecture-components.
+ *
+ * Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
+ * Once we got a notification via onChanged, we stop observing.
+ */
+@Throws(InterruptedException::class)
+fun getValue(liveData: LiveData): T {
+ val data = arrayOfNulls(1)
+ val latch = CountDownLatch(1)
+ liveData.observeForever { o ->
+ data[0] = o
+ latch.countDown()
+ }
+ latch.await(2, TimeUnit.SECONDS)
+ @Suppress("UNCHECKED_CAST")
+ return data[0] as T
+}
+
+fun LiveData.getOrAwaitValue(
+ time: Long = 2,
+ timeUnit: TimeUnit = TimeUnit.SECONDS,
+ afterObserve: () -> Unit = {},
+): T {
+ var data: T? = null
+ val latch = CountDownLatch(1)
+ val observer =
+ object : Observer {
+ override fun onChanged(value: T) {
+ data = value
+ latch.countDown()
+ this@getOrAwaitValue.removeObserver(this)
+ }
+ }
+ this.observeForever(observer)
+ afterObserve.invoke()
+ // Don't wait indefinitely if the LiveData is not set.
+ if (!latch.await(time, timeUnit)) {
+ this.removeObserver(observer)
+ throw TimeoutException("LiveData value was never set.")
+ }
+ @Suppress("UNCHECKED_CAST")
+ return data as T
+}
diff --git a/app/src/test/java/com/kom/skyfly/tools/MainCoroutineRule.kt b/app/src/test/java/com/kom/skyfly/tools/MainCoroutineRule.kt
new file mode 100644
index 0000000..bbec10c
--- /dev/null
+++ b/app/src/test/java/com/kom/skyfly/tools/MainCoroutineRule.kt
@@ -0,0 +1,24 @@
+package com.kom.skyfly.tools
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+@ExperimentalCoroutinesApi
+class MainCoroutineRule(private val dispatcher: TestDispatcher = StandardTestDispatcher()) :
+ TestWatcher() {
+ override fun starting(description: Description) {
+ super.starting(description)
+ Dispatchers.setMain(dispatcher)
+ }
+
+ override fun finished(description: Description) {
+ super.finished(description)
+ Dispatchers.resetMain()
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 46c6b40..148c9b8 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -74,6 +74,7 @@ googleGmsGoogleServices = "4.4.2"
googleFirebaseCrashlytics = "3.0.1"
firebasePerf = "21.0.1"
googleFirebaseFirebasePerf = "1.4.2"
+junitJunit = "4.12"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -158,6 +159,7 @@ toasty = { group = "com.github.GrenderG", name = "Toasty", version.ref = "toasty
swipe-refresh-layout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swipe-refresh-layout" }
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version.ref = "firebaseCrashlytics" }
firebase-perf = { group = "com.google.firebase", name = "firebase-perf", version.ref = "firebasePerf" }
+junit-junit = { group = "junit", name = "junit", version.ref = "junitJunit" }
[plugins]