diff --git a/src/jsMain/kotlin/HTMLUtils.kt b/src/jsMain/kotlin/HTMLUtils.kt new file mode 100644 index 0000000000000000000000000000000000000000..e9984968838f721ba2aa15e1f44abc664987cf1b --- /dev/null +++ b/src/jsMain/kotlin/HTMLUtils.kt @@ -0,0 +1,9 @@ +import androidx.compose.runtime.Composable +import org.jetbrains.compose.web.dom.Div + +@Composable +fun divFloatRight(content: @Composable () -> Unit) { + Div({ classes("uk-float-right", "uk-margin-right") }) { + content() + } +} \ No newline at end of file diff --git a/src/jsMain/kotlin/Main.kt b/src/jsMain/kotlin/Main.kt index e36b58520a071825d65d3217ccd23c4deac61507..08cac8cb21288074407c07737fb3c33db17ada34 100644 --- a/src/jsMain/kotlin/Main.kt +++ b/src/jsMain/kotlin/Main.kt @@ -23,7 +23,7 @@ fun main() { renderComposable(root = rootDiv) { var state by remember { mutableStateOf<State>(Ready) } - Div({ classes("uk-float-right", "uk-margin-right") }) { + divFloatRight { val magicId = "csv-magic" Button({ classes("uk-button", "uk-button-small", "uk-button-success") diff --git a/src/jsMain/kotlin/Modal.kt b/src/jsMain/kotlin/Modal.kt index 0a3d640557284758db9f6bfc7514800652ecb011..b9da873db8402a720c457c017ea1e0119f24b79f 100644 --- a/src/jsMain/kotlin/Modal.kt +++ b/src/jsMain/kotlin/Modal.kt @@ -3,8 +3,13 @@ import kotlinx.browser.document import kotlinx.browser.window import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jetbrains.compose.web.attributes.InputType +import org.jetbrains.compose.web.attributes.disabled +import org.jetbrains.compose.web.css.background +import org.jetbrains.compose.web.css.color +import org.jetbrains.compose.web.css.rgb import org.jetbrains.compose.web.dom.* import org.w3c.dom.HTMLAnchorElement import org.w3c.dom.HTMLElement @@ -24,53 +29,123 @@ fun modal(setState: (State) -> Unit) { Text("CSV import") } H4 { - Text("Paraméterek") + Text("Mi is ez?") + } + Text("Válassz ki egy CSV filet, ami megfelel a következő követelményeknek:") + Ul { + Li { Text("az első sorában a pontozási elvek szerepelnek") } + } + Ul { + Li { Text("az első oszlopa az emberek neveit tartalmazza") } + } + Ul { + Li { Text("a közte lévő rész értelemszerűen van kitöltve") } + } + Ul { + Li { Text("tartalmazhat a táblázat szumma oszlopokat, " + + "azok automatikusan ignorálva lesznek, amennyiben tartalmazzák a szumma szót") } + } + P { Text("Példa táblázat:") } + Table({classes("uk-table")}) { + Thead { Tr { + Th { Text("Név") } + Th { Text("Gyűlésen alvás") } + Th { Text("Közös liftezés") } + Th { Text("Szumma munka") } + } } + Tbody { Tr { + Td { Text("Teszt Elek") } + Td { Text("5") } + Td { Text("8") } + Td { Text("13") } + } } + Tbody { Tr { + Td { Text("Techno Kolos ") } + Td { Text("7") } + Td { Text("3") } + Td { Text("10") } + } } } - params() Hr { } val scope = rememberCoroutineScope() var csvTable by remember { mutableStateOf<CSVTable?>(null) } + + val valassz = "hint: válassz ki fájlt" + var hint by remember { mutableStateOf(valassz) } + Input(type = InputType.File, attrs = { classes("uk-button", "uk-button-small") onChange { + if (it.value.isBlank()) { + hint = valassz + return@onChange + } scope.launch { val content = it.target.getFileContents() content?.let { csv -> - csvTable = CSVTable(csv) + try { + csvTable = CSVTable(csv) + } catch (e: Exception) { + hint = "hibás CSV fájl (infó a console-ban)" + e.printStackTrace() + } + hint = "" } } } }) Hr { } - Button({ - classes("uk-button", "uk-button-small", "uk-button-danger") - onClick { - scope.launch { - PekTable.clear { setState(ImportInProgress(it)) } - setState(Ready) + divFloatRight { + Button({ + classes("uk-button", "uk-button-primary") + onClick { + scope.launch { + try { + csvTable?.process { setState(ImportInProgress(it)) } + } catch (e: ProcessingError) { + delay(100) + window.alert(e.toString()) + } + setState(Ready) + } + (document.getElementById(closeId) as HTMLElement).click() } - (document.getElementById(closeId) as HTMLElement).click() + if (hint.isNotBlank()) { + disabled() + } + }) { + Text("Zsa!") } - }) { - Text("PéKen lévő adatok törlése") } - Button({ - classes("uk-button", "uk-button-small", "uk-button-primary") - onClick { - scope.launch { - try { - csvTable?.process { setState(ImportInProgress(it)) } - } catch (e: ProcessingError) { - window.alert(e.toString()) + divFloatRight { + Button({ + classes("uk-button", "uk-button-danger") + onClick { + scope.launch { + PekTable.clear { setState(ImportInProgress(it)) } + setState(Ready) } - setState(Ready) + (document.getElementById(closeId) as HTMLElement).click() } - (document.getElementById(closeId) as HTMLElement).click() + }) { + Text("PéKen lévő adatok törlése") } - }) { - Text("Zsa!") } + divFloatRight { + Button({ + classes("uk-button") + style { + background("#0000") + color(rgb(0x55, 0x55, 0x55)) + } + disabled() + }) { + Text(hint) + } + } + + Br { } } } \ No newline at end of file diff --git a/src/jsMain/kotlin/Process.kt b/src/jsMain/kotlin/Process.kt index b1478a5a1fa3f37cb6f0608a17aa2d6d5d2f34b9..46ad42610c883ce1d13dff41c5ecfa23a8c04f42 100644 --- a/src/jsMain/kotlin/Process.kt +++ b/src/jsMain/kotlin/Process.kt @@ -1,4 +1,4 @@ -import kotlinx.coroutines.delay +import kotlin.math.max class ProcessingError(val errors: List<String>): Exception(errors.toString()) { override fun toString(): String { @@ -11,9 +11,9 @@ suspend fun CSVTable.process( progress: (Double) -> Unit = {} ) { progress(0.0) - val principleMappings = principles.map { - it to PekTable.principles.findClosestMatch(it) - }.toMap() + val principleMappings = principles.associateWith { + PekTable.principles.findClosestMatch(it) + } val errors = mutableListOf<String>() @@ -21,7 +21,7 @@ suspend fun CSVTable.process( val pekPerson = PekTable.people.findClosestMatch(person) if (pekPerson == null) { - errors += "HIBA: Nem található '$person'" + errors += "Nem található '$person'" return@forEachIndexed } principles.forEachIndexed { j, principle -> @@ -33,10 +33,10 @@ suspend fun CSVTable.process( progress(calcProgress(i, j, people.size, principles.size)) if (person != pekPerson) { - errors += "WARN: assuming '$person' == '$pekPerson'" + errors += "Assuming '$person' == '$pekPerson'" } if (principle != pekPrinciple) { - errors += "WARN: assuming '$principle' == '$pekPrinciple'" + errors += "Assuming '$principle' == '$pekPrinciple'" } } } @@ -49,9 +49,14 @@ fun List<String>.findClosestMatch( ): String? { val parts = string.split(" ", "-") return this.map { + val currParts = it.split(" ", "-") + + val needleInHaystack = currParts.count { part -> string.contains(part) } + val haystackInNeedle = parts.count { part -> it.contains(part) } + it to when { it == string -> Int.MAX_VALUE - else -> parts.count { part -> it.contains(part) } + else -> max(needleInHaystack, haystackInNeedle) } }.filter { it.second != 0 }.maxByOrNull { it.second }?.first } \ No newline at end of file diff --git a/src/jsMain/kotlin/Utils.kt b/src/jsMain/kotlin/Utils.kt index 0b387da6543e1d0edc1039fe2105aa5d2ec51b6d..50b67b3b199c41a6e7957acb88cdb86832dc9b47 100644 --- a/src/jsMain/kotlin/Utils.kt +++ b/src/jsMain/kotlin/Utils.kt @@ -96,10 +96,28 @@ suspend fun PekTable.clear( private val saveIconElem = document.getElementById("save-icon") as HTMLDivElement val isUpdating get() = saveIconElem.style.display == "block" -suspend fun pekDelay() { - delay(10) - while (isUpdating) { // még sül - delay(100) +fun observeSaveChange(changed: () -> Unit): MutationObserver { + val observer = MutationObserver { _, _ -> + changed() + } + observer.observe(saveIconElem, MutationObserverInit( + attributes = true, + attributeFilter = arrayOf("style") + )) + return observer +} + +suspend fun pekDelay() = suspendCoroutine<Unit> { cont -> + lateinit var observer: MutationObserver + observer = observeSaveChange { + if (!isUpdating) { + cont.resume(Unit) + observer.disconnect() + } + } + if (!isUpdating) { + cont.resume(Unit) + observer.disconnect() } }