Skip to content

Commit

Permalink
Merge pull request #13 from nijuyonkadesu/new_request
Browse files Browse the repository at this point in the history
feature new request
  • Loading branch information
nijuyonkadesu committed Aug 30, 2023
2 parents 345d298 + 5912b5e commit c2e2872
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ data class DatabasePendingRequest(
val origin: String,
val subject: String,
val message: String,
@ColumnInfo(name = "request_date") val requestDate: String,
val status: Stage,
val time: LocalDateTime,
val from: LocalDateTime,
Expand Down
21 changes: 21 additions & 0 deletions app/src/main/java/one/njk/celestidesk/domain/NewBreakRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package one.njk.celestidesk.domain

import one.njk.celestidesk.network.NetworkNewRequest

data class NewBreakRequest(
val subject: String,
val message: String,
var emergency: Boolean,
val from: String,
val to: String,
)

fun NewBreakRequest.asNetworkModel(): NetworkNewRequest {
return NetworkNewRequest(
this.subject,
this.message,
this.emergency,
this.from,
this.to
)
}
7 changes: 6 additions & 1 deletion app/src/main/java/one/njk/celestidesk/network/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,10 @@ interface ApiService {
@Header("Authorization") token: String,
): NetworkTransactionsContainer


@POST("api/request/create")
suspend fun createNewRequest(
@Header("Authorization") token: String,
@Body request: NetworkNewRequest
): Message
// TODO: Wrap message with sealed class
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ data class NetworkPendingRequest(
val origin: String,
val subject: String,
val message: String,
@Json(name = "requestdate") val requestDate: String,
var emergency: Boolean = false,
val status: Stage,
var time: String = "2023-07-31T13:12:01.129Z",
Expand Down Expand Up @@ -47,7 +46,6 @@ fun NetworkPendingRequestContainer.asDatabaseModel(): List<DatabasePendingReques
origin = it.origin,
subject = it.subject,
message = it.message,
requestDate = it.requestDate,
status = it.status,
time = it.time.toDate(),
from = it.from.toDate(),
Expand Down Expand Up @@ -120,4 +118,10 @@ fun NetworkTransactionsContainer.asDatabaseModel(): List<DatabaseTransaction> {

// --------------------- Transaction Model [ENDS] -------------------------- //

// TODO: Add database, domain models, mappers and a fragment with a view to show transaction (only Manager & Teamlead)
data class NetworkNewRequest(
val subject: String,
val message: String,
val emergency: Boolean,
val from: String,
val to: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,33 @@ package one.njk.celestidesk.network.auth

import android.util.Log
import one.njk.celestidesk.database.RolesDataStore
import one.njk.celestidesk.network.ApiService
import one.njk.celestidesk.network.auth.model.AuthLoginRequest
import one.njk.celestidesk.network.auth.model.AuthResult
import one.njk.celestidesk.network.auth.model.AuthSignUpRequest
import one.njk.celestidesk.network.ApiService
import retrofit2.HttpException
import one.njk.celestidesk.utils.failsafeAuth

class AuthRepositoryImpl(
private val api: ApiService,
private val pref: RolesDataStore
): AuthRepository {
override suspend fun signUp(user: AuthSignUpRequest): AuthResult<Unit> {
return try {
override suspend fun signUp(user: AuthSignUpRequest): AuthResult<Unit> =
failsafeAuth {
val response = api.signUp(user)
pref.setToken(response)
AuthResult.Authorized()
} catch (e: HttpException) {
Log.d("network", e.message.toString())

if(e.code() == 401) AuthResult.UnAuthorized()
else AuthResult.UnknownError()
} catch (e: Exception) {
AuthResult.UnknownError()
}
}

override suspend fun logIn(user: AuthLoginRequest): AuthResult<Unit> {
return try {
override suspend fun logIn(user: AuthLoginRequest): AuthResult<Unit> =
failsafeAuth {
val response = api.logIn(user)
pref.setToken(response)
Log.d("network", response.message)
AuthResult.Authorized()
} catch (e: HttpException) {
Log.d("network", e.message.toString())

if(e.code() == 401) AuthResult.UnAuthorized()
else AuthResult.UnknownError()
} catch (e: Exception) {
Log.d("network", e.message.toString())
AuthResult.UnknownError()
}
}

override suspend fun authenticate(): AuthResult<Unit> {
return try {
override suspend fun authenticate(): AuthResult<Unit> =
failsafeAuth {
// Trying out authenticate directly when not signed up case:
val token = pref.getToken()
api.authenticate("Bearer ${token.token}")
AuthResult.Authorized()

} catch (e: HttpException){
if(e.code() == 401) AuthResult.UnAuthorized()
else AuthResult.UnknownError()
} catch (e: Exception) {
AuthResult.UnknownError()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ package one.njk.celestidesk.repository

import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import one.njk.celestidesk.database.RequestsDao
import one.njk.celestidesk.database.RolesDataStore
import one.njk.celestidesk.database.TransactionDao
Expand All @@ -18,10 +14,11 @@ import one.njk.celestidesk.domain.History
import one.njk.celestidesk.network.ApiService
import one.njk.celestidesk.network.Decision
import one.njk.celestidesk.network.DecisionRequest
import one.njk.celestidesk.network.NetworkNewRequest
import one.njk.celestidesk.network.Stage
import one.njk.celestidesk.network.asDatabaseModel
import one.njk.celestidesk.utils.failsafe
import one.njk.celestidesk.utils.sendEmail
import retrofit2.HttpException
import javax.inject.Inject

class RequestRepository @Inject constructor(
Expand Down Expand Up @@ -54,7 +51,6 @@ class RequestRepository @Inject constructor(
it.asHistoryDomainModel()
}

@OptIn(FlowPreview::class)
fun allOrSearchTransactionsFlow(term: String): Flow<List<History>> {

return if(term.isNotEmpty())
Expand Down Expand Up @@ -83,21 +79,16 @@ class RequestRepository @Inject constructor(
}
}

suspend fun createNewRequest(req: NetworkNewRequest) {
failsafe {
Log.d("new", "$req")
val token = pref.getToken()
api.createNewRequest("Bearer ${token.token}", req)
}
}

fun sendMailFromRequest(subject: String, body: String, decision: Decision) {
sendEmail(subject, "Your request `$body` got $decision by MANAGER")
}
// TODO: Make it more dynamic and sensible - Replace with SMS

private suspend fun failsafe(block: suspend () -> Unit) {
withContext(Dispatchers.IO) {
try {
block()
} catch (e: HttpException) {
Log.d("network", "${e.message}")

} catch (e: Exception){
Log.d("network", "fatal: ${e.message}")
}
}
}
}
88 changes: 88 additions & 0 deletions app/src/main/java/one/njk/celestidesk/ui/NewRequestSheet.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package one.njk.celestidesk.ui

import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.core.util.Pair
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.datepicker.MaterialDatePicker
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import one.njk.celestidesk.databinding.NewRequestSheetBinding
import one.njk.celestidesk.domain.NewBreakRequest

class NewRequestSheet(val datePicker: () -> Unit, val submitRequest: (NewBreakRequest) -> Unit): BottomSheetDialogFragment() {

private lateinit var _binding: NewRequestSheetBinding

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = NewRequestSheetBinding.inflate(inflater, container, false)
return _binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val inputMethodManager =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

_binding.apply {

// Default value, coz android is headache
var isEmergency = false
emergency.setOnClickListener { _ ->
isEmergency = true
}
normal.setOnClickListener { _ ->
isEmergency = false
}
normal.performClick()

durationEdit.setOnFocusChangeListener { editText, hasFocus ->
if(hasFocus){
datePicker()
}
inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0)
}

send.setOnClickListener { _ ->
val newRequest = NewBreakRequest(
subject = subject.editText?.text.toString(),
message = reason.editText?.text.toString(),
emergency = isEmergency,
from = duration.editText?.text.toString().slice(0..9),
to = duration.editText?.text.toString().slice(13..22),
)
// Date value in edit field: 2023-08-17 - 2023-08-31
submitRequest(newRequest)
}
}
}

fun updateDateRange(range: Pair<Long, Long>?) {
val from = Instant
.fromEpochMilliseconds(
range?.first ?: MaterialDatePicker.todayInUtcMilliseconds()
).toLocalDateTime(TimeZone.UTC).date.toString()

val to = Instant
.fromEpochMilliseconds(
range?.second ?: (MaterialDatePicker.todayInUtcMilliseconds() + 86_400_000L)
).toLocalDateTime(TimeZone.UTC).date.toString()
// (24 hours * 60 minutes * 60 seconds * 1000 milliseconds)

_binding.duration.editText?.setText("$from - $to")
}
companion object {
const val TAG = "NewRequestBottomSheet"
}
}
33 changes: 28 additions & 5 deletions app/src/main/java/one/njk/celestidesk/ui/RequestFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -77,6 +79,20 @@ class RequestFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// val calendarConstraints = CalendarConstraints.Builder()
// .setStart(MaterialDatePicker.todayInUtcMilliseconds())

val dateRangePicker = MaterialDatePicker.Builder.dateRangePicker()
.setTitleText("Select dates")
// .setCalendarConstraints(calendarConstraints.build())
.build()

val requestSheet = NewRequestSheet(
{ dateRangePicker.show(childFragmentManager, "DATE_PICK") },
{ req ->
viewModel.newRequest(req)
})

lifecycleScope.launch {
_binding!!.role.text = viewModel.name
_binding!!.fab.setIconResource(fabIcon)
Expand All @@ -99,7 +115,17 @@ class RequestFragment : Fragment() {
if(currentRole != Role.EMPLOYEE)
findNavController()
.navigate(RequestFragmentDirections.actionRequestFragmentToSearchFragment())
// TODO: Create a new requests screen
else {
requestSheet.show(childFragmentManager, NewRequestSheet.TAG)
dateRangePicker.addOnDismissListener {
requestSheet.updateDateRange(dateRangePicker.selection)
}

/*
* TODO: add remaining request count near button
* TODO: disable clicking emergency request unless normal req exhausts
* */
}
}

}
Expand Down Expand Up @@ -171,7 +197,4 @@ class RequestFragment : Fragment() {
}
.show()
}
}
// TODO: Profile Pic for users
// TODO: >5 Request, it goes to Manager directly
// TODO: 20min expiry after reaching manager
}
Loading

0 comments on commit c2e2872

Please sign in to comment.