diff --git a/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/AuthSettings.kt b/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/AuthSettings.kt new file mode 100644 index 0000000..e0af931 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/AuthSettings.kt @@ -0,0 +1,35 @@ +package ch.parano.myice.auth + +import ch.parano.myice.network.AuthTokenHolder +import com.russhwolf.settings.Settings + +class AuthSettings(private val settings: Settings) { + + companion object { + private const val TOKEN_KEY = "access_token" + private const val ACCOUNT_KEY = "selected_account" + } + + fun getStoredToken(): String? = settings.getStringOrNull(TOKEN_KEY) + + fun setToken(token: String?) { + if (token != null) { + settings.putString(TOKEN_KEY, token) + } else { + settings.remove(TOKEN_KEY) + } + AuthTokenHolder.token = token + } + + fun getStoredAccount(): String? = settings.getStringOrNull(ACCOUNT_KEY) + + fun setAccount(account: String) { + settings.putString(ACCOUNT_KEY, account) + } + + fun clear() { + settings.remove(TOKEN_KEY) + settings.remove(ACCOUNT_KEY) + AuthTokenHolder.token = null + } +} diff --git a/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/OAuthClient.kt b/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/OAuthClient.kt new file mode 100644 index 0000000..07c311f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ch/parano/myice/auth/OAuthClient.kt @@ -0,0 +1,5 @@ +package ch.parano.myice.auth + +interface OAuthClient { + suspend fun authenticate(loginUrl: String): String? +} diff --git a/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/EventFilter.kt b/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/EventFilter.kt new file mode 100644 index 0000000..466bc12 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/EventFilter.kt @@ -0,0 +1,37 @@ +package ch.parano.myice.filter + +import ch.parano.myice.models.Event + +fun extractSubgroup(name: String): String { + val parts = name.split(" - ") + return if (parts.size > 1) parts.dropLast(1).joinToString(" - ") else name +} + +fun filterEvents( + events: List, + agegroup: String?, + subgroup: String?, + typeFilter: TypeFilter, +): List = events.filter { event -> + val typeMatches = when (typeFilter) { + TypeFilter.ALL -> true + TypeFilter.GAMES -> event.eventType == "Jeu" + TypeFilter.PRACTICES -> event.eventType != "Jeu" + } + if (!typeMatches) return@filter false + + if (agegroup != null && event.agegroup != agegroup) return@filter false + + if (subgroup != null) { + val eventSubgroup = extractSubgroup(event.name) + if (eventSubgroup != subgroup) return@filter false + } + + true +} + +fun List.distinctAgegroups(): List = + map { it.agegroup }.toSet().sorted() + +fun List.distinctSubgroups(): List = + map { extractSubgroup(it.name) }.toSet().sorted() diff --git a/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/TypeFilter.kt b/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/TypeFilter.kt new file mode 100644 index 0000000..62d2e2d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ch/parano/myice/filter/TypeFilter.kt @@ -0,0 +1,9 @@ +package ch.parano.myice.filter + +enum class TypeFilter { + ALL, GAMES, PRACTICES; + + companion object { + val DEFAULT = GAMES + } +} diff --git a/composeApp/src/commonTest/kotlin/ch/parano/myice/filter/EventFilterTest.kt b/composeApp/src/commonTest/kotlin/ch/parano/myice/filter/EventFilterTest.kt new file mode 100644 index 0000000..e5606bd --- /dev/null +++ b/composeApp/src/commonTest/kotlin/ch/parano/myice/filter/EventFilterTest.kt @@ -0,0 +1,157 @@ +package ch.parano.myice.filter + +import ch.parano.myice.models.Event +import kotlin.test.Test +import kotlin.test.assertEquals + +class EventFilterTest { + + private fun event( + id: String = "1", + agegroup: String = "U13 (Elite)", + name: String = "U13 Elite - HC Ajoie", + eventType: String = "Jeu", + ) = Event( + idEvent = id, + agegroup = agegroup, + name = name, + title = "Title", + opponent = "Opp", + place = "Place", + start = "2024-11-10T14:00:00", + end = "2024-11-10T16:15:00", + color = "#e4222e", + eventType = eventType, + ) + + @Test + fun testExtractSubgroupWithSeparator() { + assertEquals("U13 Elite", extractSubgroup("U13 Elite - HC Ajoie")) + } + + @Test + fun testExtractSubgroupMultipleSeparators() { + assertEquals("U13 Elite - Group A", extractSubgroup("U13 Elite - Group A - HC Ajoie")) + } + + @Test + fun testExtractSubgroupNoSeparator() { + assertEquals("U13 Elite", extractSubgroup("U13 Elite")) + } + + @Test + fun testExtractSubgroupEmptyString() { + assertEquals("", extractSubgroup("")) + } + + @Test + fun testFilterGamesOnly() { + val events = listOf( + event(id = "1", eventType = "Jeu"), + event(id = "2", eventType = ""), + event(id = "3", eventType = "Jeu"), + ) + val result = filterEvents(events, agegroup = null, subgroup = null, typeFilter = TypeFilter.GAMES) + assertEquals(2, result.size) + assertEquals("1", result[0].idEvent) + assertEquals("3", result[1].idEvent) + } + + @Test + fun testFilterPracticesOnly() { + val events = listOf( + event(id = "1", eventType = "Jeu"), + event(id = "2", eventType = ""), + event(id = "3", eventType = ""), + ) + val result = filterEvents(events, agegroup = null, subgroup = null, typeFilter = TypeFilter.PRACTICES) + assertEquals(2, result.size) + assertEquals("2", result[0].idEvent) + assertEquals("3", result[1].idEvent) + } + + @Test + fun testFilterAll() { + val events = listOf( + event(id = "1", eventType = "Jeu"), + event(id = "2", eventType = ""), + ) + val result = filterEvents(events, agegroup = null, subgroup = null, typeFilter = TypeFilter.ALL) + assertEquals(2, result.size) + } + + @Test + fun testFilterByAgegroup() { + val events = listOf( + event(id = "1", agegroup = "U13 (Elite)"), + event(id = "2", agegroup = "U15"), + event(id = "3", agegroup = "U13 (Elite)"), + ) + val result = filterEvents(events, agegroup = "U13 (Elite)", subgroup = null, typeFilter = TypeFilter.ALL) + assertEquals(2, result.size) + assertEquals("1", result[0].idEvent) + } + + @Test + fun testFilterBySubgroup() { + val events = listOf( + event(id = "1", name = "U13 Elite - HC Ajoie"), + event(id = "2", name = "U13 Top - HC Geneva"), + event(id = "3", name = "U13 Elite - HC Lausanne"), + ) + val result = filterEvents(events, agegroup = null, subgroup = "U13 Elite", typeFilter = TypeFilter.ALL) + assertEquals(2, result.size) + assertEquals("1", result[0].idEvent) + assertEquals("3", result[1].idEvent) + } + + @Test + fun testFilterCombinedAgegroupAndSubgroup() { + val events = listOf( + event(id = "1", agegroup = "U13 (Elite)", name = "U13 Elite - HC Ajoie"), + event(id = "2", agegroup = "U13 (Elite)", name = "U13 Top - HC Geneva"), + event(id = "3", agegroup = "U15", name = "U13 Elite - HC Lausanne"), + ) + val result = filterEvents(events, agegroup = "U13 (Elite)", subgroup = "U13 Elite", typeFilter = TypeFilter.ALL) + assertEquals(1, result.size) + assertEquals("1", result[0].idEvent) + } + + @Test + fun testFilterGamesAndAgegroup() { + val events = listOf( + event(id = "1", agegroup = "U13 (Elite)", eventType = "Jeu"), + event(id = "2", agegroup = "U13 (Elite)", eventType = ""), + event(id = "3", agegroup = "U15", eventType = "Jeu"), + ) + val result = filterEvents(events, agegroup = "U13 (Elite)", subgroup = null, typeFilter = TypeFilter.GAMES) + assertEquals(1, result.size) + assertEquals("1", result[0].idEvent) + } + + @Test + fun testDistinctAgegroups() { + val events = listOf( + event(agegroup = "U15"), + event(agegroup = "U13 (Elite)"), + event(agegroup = "U15"), + ) + val result = events.distinctAgegroups() + assertEquals(2, result.size) + assertEquals("U13 (Elite)", result[0]) + assertEquals("U15", result[1]) + } + + @Test + fun testDistinctSubgroups() { + val events = listOf( + event(name = "U13 Elite - HC Ajoie"), + event(name = "U13 Top - HC Geneva"), + event(name = "U13 Elite - HC Lausanne"), + ) + val result = events.distinctSubgroups() + assertEquals(2, result.size) + assertEquals("U13 Elite", result[0]) + assertEquals("U13 Top", result[1]) + } +}