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 authored Aug 30, 2023
2 parents 345d298 + 5912b5e commit c2e2872
Showing 19 changed files with 419 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -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,
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
@@ -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
@@ -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",
@@ -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(),
@@ -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
@@ -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
@@ -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
@@ -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(
@@ -54,7 +51,6 @@ class RequestRepository @Inject constructor(
it.asHistoryDomainModel()
}

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

return if(term.isNotEmpty())
@@ -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
@@ -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
@@ -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)
@@ -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
* */
}
}

}
@@ -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
Oops, something went wrong.

0 comments on commit c2e2872

Please sign in to comment.