diff --git a/app/src/main/java/com/example/belindas_closet/data/Datasource.kt b/app/src/main/java/com/example/belindas_closet/data/Datasource.kt
index bd0d652e..5156a434 100644
--- a/app/src/main/java/com/example/belindas_closet/data/Datasource.kt
+++ b/app/src/main/java/com/example/belindas_closet/data/Datasource.kt
@@ -3,10 +3,10 @@ package com.example.belindas_closet.data
import com.example.belindas_closet.R
import com.example.belindas_closet.model.Product
import com.example.belindas_closet.model.ProductGender
-import com.example.belindas_closet.model.ProductSizes
import com.example.belindas_closet.model.ProductSizePantsInseam
import com.example.belindas_closet.model.ProductSizePantsWaist
import com.example.belindas_closet.model.ProductSizeShoes
+import com.example.belindas_closet.model.ProductSizes
import com.example.belindas_closet.model.ProductType
class Datasource {
@@ -140,7 +140,7 @@ class Datasource {
ProductSizePantsInseam.XS,
"This is a handbag",
R.drawable.product12.toString(),
- "13"
+ "655b46baa82ef869be176174"
)
)
productList.add(
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/HttpRoutes.kt b/app/src/main/java/com/example/belindas_closet/data/network/HttpRoutes.kt
index ada21881..04af3c94 100644
--- a/app/src/main/java/com/example/belindas_closet/data/network/HttpRoutes.kt
+++ b/app/src/main/java/com/example/belindas_closet/data/network/HttpRoutes.kt
@@ -4,6 +4,7 @@ object HttpRoutes {
private const val BASE_URL = "http://10.0.2.2:3000/api"
const val PRODUCTS = "$BASE_URL/products"
const val PRODUCT = "$BASE_URL/products/{id}"
+ const val ARCHIVE = "$BASE_URL/products/archive"
const val LOGIN = "$BASE_URL/auth/login"
const val FORGOT_PASSWORD = "$BASE_URL/auth/forgot-password"
const val SIGNUP = "$BASE_URL/auth/signup"
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveService.kt b/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveService.kt
new file mode 100644
index 00000000..8f9d42bf
--- /dev/null
+++ b/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveService.kt
@@ -0,0 +1,42 @@
+package com.example.belindas_closet.data.network.auth
+
+import com.example.belindas_closet.MainActivity
+import com.example.belindas_closet.data.network.dto.auth_dto.ArchiveRequest
+import com.example.belindas_closet.data.network.dto.auth_dto.ArchiveResponse
+import io.ktor.client.HttpClient
+import io.ktor.client.engine.android.Android
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
+import io.ktor.client.plugins.logging.DEFAULT
+import io.ktor.client.plugins.logging.LogLevel
+import io.ktor.client.plugins.logging.Logger
+import io.ktor.client.plugins.logging.Logging
+import io.ktor.serialization.kotlinx.json.json
+import kotlinx.serialization.json.Json
+
+interface ArchiveService {
+ suspend fun archive(archiveRequest: ArchiveRequest) : ArchiveResponse?
+
+ companion object {
+ fun create() : ArchiveService {
+ return ArchiveServiceImpl(
+ client = HttpClient(Android) {
+ install(ContentNegotiation) {
+ json(Json {
+ prettyPrint = true
+ isLenient = true
+ ignoreUnknownKeys = true
+ })
+ }
+ install (Logging) {
+ level = LogLevel.ALL
+ logger = Logger.DEFAULT
+ }
+ },
+ getToken = suspend {
+ MainActivity.getPref().getString("token", "") ?: ""
+ }
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveServiceImpl.kt b/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveServiceImpl.kt
new file mode 100644
index 00000000..8e1852a2
--- /dev/null
+++ b/app/src/main/java/com/example/belindas_closet/data/network/auth/ArchiveServiceImpl.kt
@@ -0,0 +1,48 @@
+package com.example.belindas_closet.data.network.auth
+
+import com.example.belindas_closet.data.network.HttpRoutes
+import com.example.belindas_closet.data.network.dto.auth_dto.ArchiveRequest
+import com.example.belindas_closet.data.network.dto.auth_dto.ArchiveResponse
+import io.ktor.client.HttpClient
+import io.ktor.client.call.body
+import io.ktor.client.plugins.ClientRequestException
+import io.ktor.client.plugins.RedirectResponseException
+import io.ktor.client.plugins.ServerResponseException
+import io.ktor.client.request.header
+import io.ktor.client.request.put
+import io.ktor.client.request.url
+import io.ktor.http.ContentType
+import io.ktor.http.HttpHeaders
+import io.ktor.util.InternalAPI
+import kotlinx.serialization.json.Json
+
+class ArchiveServiceImpl (
+ private val client: HttpClient,
+ private val getToken: suspend () -> String
+) : ArchiveService {
+ @OptIn(InternalAPI::class)
+ override suspend fun archive(archiveRequest: ArchiveRequest): ArchiveResponse? {
+ return try {
+ val token = getToken()
+ val response = client.put {
+ url("${HttpRoutes.ARCHIVE}/${archiveRequest.id}")
+ header(HttpHeaders.ContentType, ContentType.Application.Json.toString())
+ header(HttpHeaders.Authorization, "Bearer $token")
+ body = Json.encodeToString(ArchiveRequest.serializer(), archiveRequest)
+ }
+ response.body()
+ } catch (e: RedirectResponseException) {
+ println("Error: ${e.response.status.description}")
+ null
+ } catch (e: ClientRequestException) {
+ println("Error: ${e.response.status.description}")
+ null
+ } catch (e: ServerResponseException) {
+ println("Error: ${e.response.status.description}")
+ null
+ } catch (e: Exception) {
+ println("Error: ${e.message}")
+ null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/auth/LoginService.kt b/app/src/main/java/com/example/belindas_closet/data/network/auth/LoginService.kt
index 68edb892..da1faefc 100644
--- a/app/src/main/java/com/example/belindas_closet/data/network/auth/LoginService.kt
+++ b/app/src/main/java/com/example/belindas_closet/data/network/auth/LoginService.kt
@@ -13,6 +13,7 @@ import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
interface LoginService {
+
suspend fun login(loginRequest: LoginRequest) : LoginResponse?
companion object {
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveRequest.kt b/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveRequest.kt
new file mode 100644
index 00000000..41311d4c
--- /dev/null
+++ b/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveRequest.kt
@@ -0,0 +1,13 @@
+package com.example.belindas_closet.data.network.dto.auth_dto
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ArchiveRequest(
+ @SerialName("id")
+ val id: String,
+
+ @SerialName("role")
+ val role: Role
+)
diff --git a/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveResponse.kt b/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveResponse.kt
new file mode 100644
index 00000000..7ca25197
--- /dev/null
+++ b/app/src/main/java/com/example/belindas_closet/data/network/dto/auth_dto/ArchiveResponse.kt
@@ -0,0 +1,10 @@
+package com.example.belindas_closet.data.network.dto.auth_dto
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ArchiveResponse(
+ @SerialName("isSold")
+ val isSold: Boolean = false
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/belindas_closet/screen/IndividualProductUpdate.kt b/app/src/main/java/com/example/belindas_closet/screen/IndividualProductUpdate.kt
index 25ad9bf1..67667174 100644
--- a/app/src/main/java/com/example/belindas_closet/screen/IndividualProductUpdate.kt
+++ b/app/src/main/java/com/example/belindas_closet/screen/IndividualProductUpdate.kt
@@ -1,5 +1,8 @@
package com.example.belindas_closet.screen
+import android.content.Context
+import android.net.http.HttpException
+import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@@ -34,25 +37,27 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.navigation.NavController
import com.example.belindas_closet.MainActivity
import com.example.belindas_closet.R
import com.example.belindas_closet.Routes
import com.example.belindas_closet.data.Datasource
+import com.example.belindas_closet.data.network.auth.ArchiveService
+import com.example.belindas_closet.data.network.dto.auth_dto.ArchiveRequest
+import com.example.belindas_closet.data.network.dto.auth_dto.Role
import com.example.belindas_closet.model.Product
import com.example.belindas_closet.model.ProductSizes
+import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -99,6 +104,8 @@ fun UpdateIndividualProductCard(product: Product, navController: NavController)
var isArchive by remember { mutableStateOf(false) }
var isSave by remember { mutableStateOf(false) }
var isCancel by remember { mutableStateOf(false) }
+ val coroutineScope = rememberCoroutineScope()
+ val current = LocalContext.current
Card(
modifier = Modifier
@@ -210,8 +217,9 @@ fun UpdateIndividualProductCard(product: Product, navController: NavController)
editor.putStringSet("hidden", hidden)
editor.apply()
navController.navigate(Routes.ProductDetail.route)
- // TODO: Add the product to "sold" collection in database
- // Remove the product from product page
+ coroutineScope.launch {
+ archive(product.id, navController, current)
+ }
isArchive = false
}, onDismiss = {
isArchive = false
@@ -335,7 +343,7 @@ fun ConfirmationArchiveDialogIndividual(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text(stringResource(R.string.update_confirm_confirm_text))
+ Text(stringResource(R.string.update_archive_confirm_text))
Spacer(modifier = Modifier.padding(8.dp))
Row(
modifier = Modifier
@@ -447,4 +455,27 @@ fun ConfirmCancelDialogIndividual(
}
}
}
+}
+
+suspend fun archive(productId: String, navController: NavController, current: Context) {
+ return try {
+ val archiveRequest = ArchiveRequest(
+ id = productId,
+ role = Role.ADMIN
+ )
+ val archiveResponse = ArchiveService.create().archive(archiveRequest)
+ if (productId.count() != 24) {
+ Toast.makeText(current, R.string.archive_invalid_id, Toast.LENGTH_SHORT).show()
+ } else if (archiveResponse != null) {
+ MainActivity.getPref().edit().putBoolean("isSold", archiveResponse.isSold).apply()
+ navController.navigate(Routes.ProductDetail.route)
+ Toast.makeText(current, R.string.archive_successful_toast, Toast.LENGTH_SHORT).show()
+ } else {
+ Toast.makeText(current, R.string.archive_failed_toast, Toast.LENGTH_SHORT).show()
+ }
+ } catch (e: HttpException) {
+ e.printStackTrace()
+ println("Error: ${e.message}")
+ Toast.makeText(current, "Archive failed. Error: ${e.message}", Toast.LENGTH_SHORT).show()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/belindas_closet/screen/Login.kt b/app/src/main/java/com/example/belindas_closet/screen/Login.kt
index b6adc3b2..ed3fbd0d 100644
--- a/app/src/main/java/com/example/belindas_closet/screen/Login.kt
+++ b/app/src/main/java/com/example/belindas_closet/screen/Login.kt
@@ -301,11 +301,12 @@ fun NSCMascot() {
}
suspend fun loginWithValidCredentials(email: String, password: String, navController: NavHostController, current: Context) {
- // login with valid credentials
try {
val loginRequest = LoginRequest(email, password)
val loginResponse = LoginService.create().login(loginRequest)
if (loginResponse != null) {
+ val token = loginResponse.token
+ saveToken(token)
MainActivity.getPref().edit().putString("token", loginResponse.token).apply()
navController.navigate(Routes.AdminView.route)
Toast.makeText(
@@ -313,6 +314,7 @@ suspend fun loginWithValidCredentials(email: String, password: String, navContro
"Welcome ${getName(loginResponse.token)} to Belinda's Closet!",
Toast.LENGTH_SHORT
).show()
+ loginResponse.token
} else {
Toast.makeText(
current,
@@ -341,3 +343,7 @@ fun getName(token: String): String? {
null
}
}
+
+fun saveToken(token: String) {
+ MainActivity.getPref().edit().putString("token", token).apply()
+}
diff --git a/app/src/main/java/com/example/belindas_closet/screen/ProductDetail.kt b/app/src/main/java/com/example/belindas_closet/screen/ProductDetail.kt
index 33749bd3..09eeb3e9 100644
--- a/app/src/main/java/com/example/belindas_closet/screen/ProductDetail.kt
+++ b/app/src/main/java/com/example/belindas_closet/screen/ProductDetail.kt
@@ -1,23 +1,14 @@
package com.example.belindas_closet.screen
-import android.app.Activity
import android.content.res.Resources
-import android.util.DisplayMetrics
-import android.view.Display
-import android.view.Window
-import android.view.WindowManager
-import android.view.WindowMetrics
import androidx.compose.foundation.Image
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
@@ -34,29 +25,22 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.capitalize
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.belindas_closet.MainActivity
import com.example.belindas_closet.R
import com.example.belindas_closet.Routes
-import com.example.belindas_closet.model.Product
import com.example.belindas_closet.data.Datasource
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.material3.rememberDrawerState
-import androidx.compose.ui.text.style.TextOverflow
+import com.example.belindas_closet.model.Product
@OptIn(ExperimentalMaterial3Api::class)
@Composable
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d0c105dd..98e1889d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -41,10 +41,13 @@
Are you sure you want to delete this product?
- Are you sure you want to archive this product?
+ Are you sure you want to archive this product?
Are you sure you want to save this product?
Are you sure you want to cancel this edit?
Update Product
+ Archival successful!
+ Invalid ID!
+ Archival failed, please try again!
Forgot Password?