load selectValues from Tags-endpoint
This commit is contained in:
1 parent
2434383953
commit
af0d8b77a5
7 files changed
+47
-27
No files matched your search
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="com.nbadal.ktlint.KtlintProjectSettings">
|
||||||
|
<ktlintRulesetVersion>DEFAULT</ktlintRulesetVersion>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -27,6 +27,11 @@ GET http://localhost:31009/v1/spaces/{{spaceId}}/properties/{{priorityPropertyId
|
|||||||
Authorization: Bearer {{apiKey}}
|
Authorization: Bearer {{apiKey}}
|
||||||
Anytype-Version: 0.1
|
Anytype-Version: 0.1
|
||||||
|
|
||||||
|
### Get Property Tags
|
||||||
|
GET http://localhost:31009/v1/spaces/{{spaceId}}/properties/{{priorityPropertyId}}/tags
|
||||||
|
Authorization: Bearer {{apiKey}}
|
||||||
|
Anytype-Version: 0.1
|
||||||
|
|
||||||
### Get Templates
|
### Get Templates
|
||||||
GET http://localhost:31009/v1/spaces/{{spaceId}}/types/{{taskTypeId}}/templates
|
GET http://localhost:31009/v1/spaces/{{spaceId}}/types/{{taskTypeId}}/templates
|
||||||
Authorization: Bearer {{apiKey}}
|
Authorization: Bearer {{apiKey}}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ private fun setupSystemTray(onShow: () -> Unit, onSettings: () -> Unit, onExit:
|
|||||||
val icon = createTrayIcon(size)
|
val icon = createTrayIcon(size)
|
||||||
|
|
||||||
val menu = PopupMenu().apply {
|
val menu = PopupMenu().apply {
|
||||||
add(MenuItem("Open").also { it.addActionListener { onShow() } })
|
add(MenuItem("Create Task").also { it.addActionListener { onShow() } })
|
||||||
add(MenuItem("Settings").also { it.addActionListener { onSettings() } })
|
add(MenuItem("Settings").also { it.addActionListener { onSettings() } })
|
||||||
addSeparator()
|
addSeparator()
|
||||||
add(MenuItem("Quit").also { it.addActionListener { onExit() } })
|
add(MenuItem("Quit").also { it.addActionListener { onExit() } })
|
||||||
|
|||||||
@@ -88,6 +88,15 @@ class AnyTypeApi(
|
|||||||
response.body<SearchObjectsResponse>().data
|
response.body<SearchObjectsResponse>().data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getTags(propertyId: String): Result<List<Tag>> =
|
||||||
|
runCatching {
|
||||||
|
val response = http.get("$baseUrl/v1/spaces/$spaceId/properties/$propertyId/tags") {
|
||||||
|
header("Anytype-Version", "2025-11-08")
|
||||||
|
bearerAuth(apiKey)
|
||||||
|
}
|
||||||
|
response.body<TagsResponse>().data
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTemplates(typeId: String): Result<List<Template>> =
|
suspend fun getTemplates(typeId: String): Result<List<Template>> =
|
||||||
runCatching {
|
runCatching {
|
||||||
val response = http.get("$baseUrl/v1/spaces/$spaceId/types/$typeId/templates") {
|
val response = http.get("$baseUrl/v1/spaces/$spaceId/types/$typeId/templates") {
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ data class Connection(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class PropertyConfig(
|
data class PropertyConfig(
|
||||||
val key: String,
|
val key: String,
|
||||||
|
val id: String = "",
|
||||||
val name: String = "",
|
val name: String = "",
|
||||||
val format: String = "",
|
val format: String = "",
|
||||||
val indicator: String = "",
|
val indicator: String = "",
|
||||||
@SerialName("possible_values") val possibleValues: List<String> = emptyList(),
|
|
||||||
@SerialName("object_type_key") val objectTypeKey: String = "",
|
@SerialName("object_type_key") val objectTypeKey: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -364,7 +364,6 @@ private fun PropertyTable(
|
|||||||
Text("Name", fontWeight = FontWeight.Bold, modifier = Modifier.weight(2f))
|
Text("Name", fontWeight = FontWeight.Bold, modifier = Modifier.weight(2f))
|
||||||
Text("Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
|
Text("Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
|
||||||
Text("Ind.", fontWeight = FontWeight.Bold, modifier = Modifier.weight(0.7f))
|
Text("Ind.", fontWeight = FontWeight.Bold, modifier = Modifier.weight(0.7f))
|
||||||
Text("Values", fontWeight = FontWeight.Bold, modifier = Modifier.weight(2f))
|
|
||||||
Text("Obj Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
|
Text("Obj Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
|
||||||
Spacer(modifier = Modifier.width(40.dp))
|
Spacer(modifier = Modifier.width(40.dp))
|
||||||
}
|
}
|
||||||
@@ -383,11 +382,6 @@ private fun PropertyTable(
|
|||||||
Text(prop.name, modifier = Modifier.weight(2f), maxLines = 1)
|
Text(prop.name, modifier = Modifier.weight(2f), maxLines = 1)
|
||||||
Text(prop.format, modifier = Modifier.weight(1.5f), maxLines = 1)
|
Text(prop.format, modifier = Modifier.weight(1.5f), maxLines = 1)
|
||||||
Text(prop.indicator, modifier = Modifier.weight(0.7f), maxLines = 1)
|
Text(prop.indicator, modifier = Modifier.weight(0.7f), maxLines = 1)
|
||||||
Text(
|
|
||||||
prop.possibleValues.joinToString(", "),
|
|
||||||
modifier = Modifier.weight(2f),
|
|
||||||
maxLines = 1,
|
|
||||||
)
|
|
||||||
Text(prop.objectTypeKey, modifier = Modifier.weight(1.5f), maxLines = 1)
|
Text(prop.objectTypeKey, modifier = Modifier.weight(1.5f), maxLines = 1)
|
||||||
IconButton(onClick = { onDelete(index) }) {
|
IconButton(onClick = { onDelete(index) }) {
|
||||||
Icon(Icons.Default.Delete, contentDescription = "Delete", tint = Color.Gray)
|
Icon(Icons.Default.Delete, contentDescription = "Delete", tint = Color.Gray)
|
||||||
@@ -409,7 +403,6 @@ private fun PropertyEditPanel(
|
|||||||
) {
|
) {
|
||||||
var selectedPropertyKey by remember(existing) { mutableStateOf(existing?.key ?: "") }
|
var selectedPropertyKey by remember(existing) { mutableStateOf(existing?.key ?: "") }
|
||||||
var indicator by remember(existing) { mutableStateOf(existing?.indicator ?: "") }
|
var indicator by remember(existing) { mutableStateOf(existing?.indicator ?: "") }
|
||||||
var possibleValuesText by remember(existing) { mutableStateOf(existing?.possibleValues?.joinToString("\n") ?: "") }
|
|
||||||
var objectTypeKey by remember(existing) { mutableStateOf(existing?.objectTypeKey ?: "") }
|
var objectTypeKey by remember(existing) { mutableStateOf(existing?.objectTypeKey ?: "") }
|
||||||
|
|
||||||
val selectedProperty = availableProperties.find { it.key == selectedPropertyKey }
|
val selectedProperty = availableProperties.find { it.key == selectedPropertyKey }
|
||||||
@@ -446,15 +439,6 @@ private fun PropertyEditPanel(
|
|||||||
|
|
||||||
// Format-specific fields
|
// Format-specific fields
|
||||||
when (format) {
|
when (format) {
|
||||||
"select", "multi_select" -> {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = possibleValuesText,
|
|
||||||
onValueChange = { possibleValuesText = it },
|
|
||||||
label = { Text("Possible Values (one per line)") },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(120.dp),
|
|
||||||
maxLines = 10,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"object", "objects" -> {
|
"object", "objects" -> {
|
||||||
DropdownSelector(
|
DropdownSelector(
|
||||||
label = "Object Type",
|
label = "Object Type",
|
||||||
@@ -470,14 +454,13 @@ private fun PropertyEditPanel(
|
|||||||
TextButton(onClick = onCancel) { Text("Cancel") }
|
TextButton(onClick = onCancel) { Text("Cancel") }
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val values = possibleValuesText.lines().map { it.trim() }.filter { it.isNotEmpty() }
|
|
||||||
onConfirm(
|
onConfirm(
|
||||||
PropertyConfig(
|
PropertyConfig(
|
||||||
key = selectedPropertyKey,
|
key = selectedPropertyKey,
|
||||||
|
id = selectedProperty?.id ?: existing?.id ?: "",
|
||||||
name = selectedProperty?.name ?: existing?.name ?: "",
|
name = selectedProperty?.name ?: existing?.name ?: "",
|
||||||
format = format,
|
format = format,
|
||||||
indicator = indicator,
|
indicator = indicator,
|
||||||
possibleValues = values,
|
|
||||||
objectTypeKey = objectTypeKey,
|
objectTypeKey = objectTypeKey,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import androidx.compose.ui.input.key.type
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ch.ergon.anytask.api.AnyTypeApi
|
import ch.ergon.anytask.api.AnyTypeApi
|
||||||
import ch.ergon.anytask.api.dto.SearchObject
|
import ch.ergon.anytask.api.dto.SearchObject
|
||||||
|
import ch.ergon.anytask.api.dto.Tag
|
||||||
import ch.ergon.anytask.config.Configuration
|
import ch.ergon.anytask.config.Configuration
|
||||||
import ch.ergon.anytask.api.dto.buildPropertyValue
|
import ch.ergon.anytask.api.dto.buildPropertyValue
|
||||||
import ch.ergon.anytask.config.PropertyConfig
|
import ch.ergon.anytask.config.PropertyConfig
|
||||||
@@ -62,6 +63,7 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
|
|||||||
var isLoading by remember { mutableStateOf(false) }
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
var errorMessage by remember { mutableStateOf<String?>(null) }
|
var errorMessage by remember { mutableStateOf<String?>(null) }
|
||||||
val propertyValues = remember { mutableStateMapOf<String, String>() }
|
val propertyValues = remember { mutableStateMapOf<String, String>() }
|
||||||
|
val propertyTags = remember { mutableStateMapOf<String, List<Tag>>() }
|
||||||
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
@@ -144,6 +146,7 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
|
|||||||
value = propertyValues[prop.key] ?: "",
|
value = propertyValues[prop.key] ?: "",
|
||||||
onValueChange = { propertyValues[prop.key] = it },
|
onValueChange = { propertyValues[prop.key] = it },
|
||||||
client = client,
|
client = client,
|
||||||
|
tags = propertyTags[prop.key] ?: emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +177,19 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(isVisible) { if (isVisible) focusRequester.requestFocus() }
|
LaunchedEffect(isVisible) {
|
||||||
|
if (isVisible) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
config.properties
|
||||||
|
.filter { it.format in listOf("select", "multi_select") && it.id.isNotEmpty() }
|
||||||
|
.forEach { prop ->
|
||||||
|
launch {
|
||||||
|
client.getTags(prop.id)
|
||||||
|
.onSuccess { propertyTags[prop.key] = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -183,13 +198,14 @@ private fun PropertyField(
|
|||||||
value: String,
|
value: String,
|
||||||
onValueChange: (String) -> Unit,
|
onValueChange: (String) -> Unit,
|
||||||
client: AnyTypeApi,
|
client: AnyTypeApi,
|
||||||
|
tags: List<Tag> = emptyList(),
|
||||||
) {
|
) {
|
||||||
when (prop.format) {
|
when (prop.format) {
|
||||||
"select", "multi_select" -> {
|
"select", "multi_select" -> {
|
||||||
SelectField(
|
SelectField(
|
||||||
label = prop.name,
|
label = prop.name,
|
||||||
value = value,
|
value = value,
|
||||||
options = prop.possibleValues,
|
tags = tags,
|
||||||
onValueChange = onValueChange,
|
onValueChange = onValueChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -231,16 +247,17 @@ private fun PropertyField(
|
|||||||
private fun SelectField(
|
private fun SelectField(
|
||||||
label: String,
|
label: String,
|
||||||
value: String,
|
value: String,
|
||||||
options: List<String>,
|
tags: List<Tag>,
|
||||||
onValueChange: (String) -> Unit,
|
onValueChange: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
val selectedName = tags.find { it.id == value }?.name ?: value
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Text(label, style = MaterialTheme.typography.caption)
|
Text(label, style = MaterialTheme.typography.caption)
|
||||||
Box {
|
Box {
|
||||||
OutlinedButton(onClick = { expanded = true }, modifier = Modifier.fillMaxWidth()) {
|
OutlinedButton(onClick = { expanded = true }, modifier = Modifier.fillMaxWidth()) {
|
||||||
Text(value.ifEmpty { "Select..." })
|
Text(selectedName.ifEmpty { "Select..." })
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
@@ -249,12 +266,12 @@ private fun SelectField(
|
|||||||
}) {
|
}) {
|
||||||
Text("(none)", style = MaterialTheme.typography.caption)
|
Text("(none)", style = MaterialTheme.typography.caption)
|
||||||
}
|
}
|
||||||
options.forEach { option ->
|
tags.forEach { tag ->
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
onValueChange(option)
|
onValueChange(tag.id)
|
||||||
expanded = false
|
expanded = false
|
||||||
}) {
|
}) {
|
||||||
Text(option)
|
Text(tag.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||