Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions composeApp/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
<string name="action_take_new_photo">Take new photo</string>
<string name="action_upload">Upload</string>
<string name="action_yes">Yes</string>
<string name="action_add_meal_type">Add new meal type</string>
<string name="action_edit_meal_type">Edit meal type</string>

<string name="app_name">kitshn</string>

Expand All @@ -111,6 +113,7 @@
<string name="common_api_token">API token</string>
<string name="common_by">by</string>
<string name="common_category">Category</string>
<string name="common_color">Color</string>
<string name="common_comment">Comment</string>
<string name="common_contact">Contact</string>
<string name="common_cook_book">Cookbook</string>
Expand Down Expand Up @@ -194,6 +197,7 @@
<string name="common_supermarket">Supermarket</string>
<string name="common_shopping_list">Shopping list</string>
<string name="common_tags">Tags</string>
<string name="common_time">Time</string>
<string name="common_time_wait">Waiting time</string>
<string name="common_time_work">Working time</string>
<string name="common_title">Title</string>
Expand Down Expand Up @@ -445,6 +449,25 @@
<string name="tandoor_compatibility_unknown_description">The version of your Tandoor server has not yet been checked. The functionality of the app may be impaired.</string>
<string name="tandoor_compatibility_unknown_label">Compatibility unknown</string>


<string name="color_light_red">Light Red</string>
<string name="color_magenta">Magenta</string>
<string name="color_lila">Lavender</string>
<string name="color_blue">Blue</string>
<string name="color_teal">Teal</string>
<string name="color_red">Red</string>
<string name="color_green">Green</string>
<string name="color_olive">Olive</string>
<string name="color_gray">Gray</string>
<string name="color_black">Black</string>
<string name="color_white">White</string>
<string name="color_dark_gray">Dark Gray</string>
<string name="color_light_gray">Light Gray</string>
<string name="color_cyan">Cyan</string>
<string name="color_yellow">Yellow</string>



<string-array name="timer_detection_hour_definitions">
<item>hours</item>
<item>hour</item>
Expand Down
25 changes: 25 additions & 0 deletions composeApp/src/commonMain/kotlin/de/kitshn/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import co.touchlab.kermit.Logger
Expand All @@ -37,6 +38,7 @@ import kotlinx.coroutines.delay
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.LocalTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.format
Expand Down Expand Up @@ -312,6 +314,29 @@ fun Long.toLocalDate(
.toLocalDateTime(timeZone).date
}

enum class TimePrecision { NONE, SECONDS, MINUTES, HOURS, DAY, MONTH, YEAR}

/**
* Provides a rounded date and time.
*/
fun getRoundedDateTime(
precision: TimePrecision = TimePrecision.NONE,
clock: Clock = Clock.System,
timeZone: TimeZone = TimeZone.currentSystemDefault()
): LocalDateTime {
val now = clock.now().toLocalDateTime(timeZone)

return when (precision) {
TimePrecision.NONE -> now
TimePrecision.SECONDS -> LocalDateTime(now.year, now.monthNumber, now.dayOfMonth, now.hour, now.minute, now.second)
TimePrecision.MINUTES -> LocalDateTime(now.year, now.monthNumber, now.dayOfMonth, now.hour, now.minute)
TimePrecision.HOURS -> LocalDateTime(now.year, now.monthNumber, now.dayOfMonth, now.hour, 0)
TimePrecision.DAY -> LocalDateTime(now.year, now.monthNumber, now.dayOfMonth, 0, 0)
TimePrecision.MONTH -> LocalDateTime(now.year, now.monthNumber, 1, 0, 0)
TimePrecision.YEAR -> LocalDateTime(now.year, 1, 1, 0, 0)
}
}

@Composable
expect fun LocalDate.format(pattern: String): String

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.kitshn.api.tandoor.model

import androidx.compose.ui.graphics.Color
import com.materialkolor.ktx.toHex
import de.kitshn.api.tandoor.TandoorClient
import de.kitshn.api.tandoor.delete
import de.kitshn.api.tandoor.model.recipe.TandoorRecipeOverview
Expand All @@ -10,6 +11,7 @@ import de.kitshn.parseTandoorDate
import de.kitshn.toColorInt
import de.kitshn.toStartOfDayString
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalTime
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
Expand All @@ -33,6 +35,42 @@ data class TandoorMealType(
} else {
Color(colorStr?.toColorInt() ?: -1)
}

suspend fun delete(client: TandoorClient): String {
val response = client.delete("/meal-type/${id}/")

if (response.status.value in 200..299) {
client.container.mealType.remove(id)
}

return response.status.value.toString()
}

suspend fun partialUpdate(
client: TandoorClient,
name: String? = null,
order: Int? = null,
time: LocalTime? = null,
color: Color? = null,
): TandoorMealType {
val data = buildJsonObject {
if(name != null) put("name", JsonPrimitive(name))
if(order != null) put("order", json.encodeToJsonElement(order))
if(time != null) put("time", JsonPrimitive(time.toString()))
if(color != null) put("color", JsonPrimitive(color.toHex()))
}

val updated = parse(client.patchObject("/meal-type/${id}/", data).toString())
client.container.mealType[updated.id] = updated
return updated
}

companion object {
fun parse(data: String): TandoorMealType {
val obj = json.decodeFromString<TandoorMealType>(data)
return obj
}
}
}

@Serializable
Expand All @@ -59,6 +97,7 @@ class TandoorMealPlan(
val obj = json.decodeFromString<TandoorMealPlan>(data)
obj.client = client
obj.recipe?.let { client.container.recipeOverview[it.id] = it }
obj.meal_type.let { client.container.mealType[it.id] = it }
return obj
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package de.kitshn.api.tandoor.route

import androidx.compose.ui.graphics.Color
import com.materialkolor.ktx.toHex
import de.kitshn.api.tandoor.TandoorClient
import de.kitshn.api.tandoor.getObject
import de.kitshn.api.tandoor.model.TandoorMealType
import de.kitshn.api.tandoor.model.TandoorPagedResponse
import de.kitshn.api.tandoor.patchObject
import de.kitshn.api.tandoor.postObject
import de.kitshn.json
import kotlinx.datetime.LocalTime
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.encodeToJsonElement

class TandoorMealTypeRoute(client: TandoorClient) : TandoorBaseRoute(client) {

Expand All @@ -20,4 +28,25 @@ class TandoorMealTypeRoute(client: TandoorClient) : TandoorBaseRoute(client) {
return response.results
}

suspend fun create(
name: String,
order: Int?,
time: LocalTime? = null,
color: Color? = null,
): TandoorMealType {
val data = buildJsonObject {
put("name", JsonPrimitive(name))
if(order != null) put("order", json.encodeToJsonElement(order))
if(time != null) put("time", JsonPrimitive(time.toString()))
if(color != null) put("color", JsonPrimitive(color.toHex()))
}

val mealType = TandoorMealType.parse(
client.postObject("/meal-type/", data).toString()
)

client.container.mealType[mealType.id] = mealType

return mealType
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package de.kitshn.model.form.item.field

import androidx.compose.foundation.layout.Row
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import de.kitshn.ui.component.input.ColorPickerField
import kitshn.composeapp.generated.resources.Res
import kitshn.composeapp.generated.resources.form_error_field_empty
import org.jetbrains.compose.resources.getString

class KitshnFormColorFieldItem(
val value: () -> Color?,
val onValueChange: (value: Color?) -> Unit,

label: @Composable () -> Unit,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
prefix: @Composable (() -> Unit)? = null,
suffix: @Composable (() -> Unit)? = null,

optional: Boolean = false,

val check: (value: Color?) -> String?
) : KitshnFormBaseFieldItem(
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
prefix = prefix,
suffix = suffix,
optional = optional
) {

@Composable
override fun Render(
modifier: Modifier
) {
var error by rememberSaveable { mutableStateOf<String?>(null) }
val value = value()

ColorPickerField(
modifier = modifier,

value = value,
label = {
Row {
label()
if(!optional) Text("*")
}
},

placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
prefix = prefix,
suffix = suffix,

isError = error != null || generalError != null,
supportingText = if(error != null || generalError != null) {
{
Text(
text = error ?: generalError ?: "",
color = MaterialTheme.colorScheme.error
)
}
} else null,

onValueChange = {
generalError = null
onValueChange(it)

error = if(it == null)
null
else
check(it)
}
)
}

override suspend fun submit(): Boolean {
val value = value()
val checkResult = check(value)

if(!optional && value == null) {
generalError = getString(Res.string.form_error_field_empty)
return false
} else if(checkResult != null) {
generalError = checkResult
return false
} else {
generalError = null
return true
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import de.kitshn.api.tandoor.TandoorClient
import de.kitshn.ui.component.input.MealTypeSearchField
import de.kitshn.ui.component.input.MealTypePickerField
import kitshn.composeapp.generated.resources.Res
import kitshn.composeapp.generated.resources.form_error_field_empty
import org.jetbrains.compose.resources.getString
Expand Down Expand Up @@ -55,11 +55,10 @@ class KitshnFormMealTypeSearchFieldItem(
var error by rememberSaveable { mutableStateOf<String?>(null) }
val value = value()

MealTypeSearchField(
MealTypePickerField(
modifier = modifier.fillMaxWidth(),
client = client,
value = value,
useDefaultMealTypeIfNull = true,
label = {
Row {
label()
Expand Down Expand Up @@ -89,15 +88,14 @@ class KitshnFormMealTypeSearchFieldItem(
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),

onValueChange = {
generalError = null
onValueChange(it)

error = if(it == null)
null
else
check(it)

}
)
}
Expand All @@ -118,4 +116,4 @@ class KitshnFormMealTypeSearchFieldItem(
}
}

}
}
Loading