From 84b321684138fe9b211ca86ca7035b2afd44cca6 Mon Sep 17 00:00:00 2001 From: Cramsan Date: Wed, 22 Jul 2026 17:58:49 -0700 Subject: [PATCH 1/4] [EDIFIKANA] Add Properties count row to Organization Detail (#542) Computes the per-org property count via PropertyManager.getPropertyList() filtered to orgId, matching the pattern already used in MyOrganizationsViewModel, and renders it in OrgDetailScreen's InfoCard between Members and Joined to match the mockup. --- .../OrgDetailScreenPreview_Desktop.png | 4 +-- .../OrgDetailScreenPreview_Phone.png | 4 +-- .../OrgDetailScreenPreview_Tablet.png | 4 +-- .../OrgDetailScreenSoleOwnerPreview.png | 4 +-- .../orgdetail/OrgDetailScreen.kt | 3 ++ .../orgdetail/OrgDetailScreenPreview.kt | 2 ++ .../orgdetail/OrgDetailUIState.kt | 2 ++ .../orgdetail/OrgDetailViewModel.kt | 9 +++++ .../orgdetail/OrgDetailViewModelTest.kt | 36 +++++++++++++++++++ 9 files changed, 60 insertions(+), 8 deletions(-) diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png index 65b6952f4..088668813 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34805cca2294290f1b3739bfff935dfba8cbd56c89f6be843f1c0517bee0a413 -size 105694 +oid sha256:83c937931bf6b37f9f1c80f49338c633afb5550833101f176b5802c2c4dade46 +size 110434 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png index 492884ada..4b9c3eaad 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c999754880dd90e819ffd9759843570a07c7754fc9d221b842af5f115750ee4 -size 76139 +oid sha256:922d268d06984308aeeebdd958726063143eb64967503b3f48e605a6c291263d +size 79838 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png index 3b4c99e7a..5a1446ded 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a2bcec4313a0a9cf6f89a18837e57de08c5091ccb33e1a44d173cfaaf899757 -size 93241 +oid sha256:a8f1a330f0f753d893c23650739933bacbc14735698f7bc8fa172919a7ca048d +size 97754 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png index c72819301..f6de80f56 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa9a373c39190520cb68c926031944c841b8ce7eee5ecaa2746fe53f7e273832 -size 74808 +oid sha256:2ede9f4f6285095ecfecd89b417d5e868c8d471f8917c1c65ee7f07ef5f2937b +size 77953 diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt index 3043024e0..86a08b76c 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt @@ -126,6 +126,7 @@ internal fun OrgDetailContent( InfoCard( userRole = uiState.userRole, memberCount = uiState.memberCount, + propertyCount = uiState.propertyCount, joinedDate = uiState.joinedDate, modifier = sectionModifier, ) @@ -186,6 +187,7 @@ private fun OrgHeaderCard( private fun InfoCard( userRole: OrgRole?, memberCount: Int, + propertyCount: Int, joinedDate: String, modifier: Modifier = Modifier, ) { @@ -196,6 +198,7 @@ private fun InfoCard( value = userRole?.name?.lowercase()?.replaceFirstChar { it.uppercaseChar() } ?: "—", ) InfoRow(label = "Members", value = memberCount.toString()) + InfoRow(label = "Properties", value = propertyCount.toString()) InfoRow(label = "Joined", value = joinedDate.ifEmpty { "—" }) } } diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreenPreview.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreenPreview.kt index 2f0e2a784..66c11356d 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreenPreview.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreenPreview.kt @@ -18,6 +18,7 @@ private fun OrgDetailScreenPreview() = isActiveOrg = true, userRole = OrgRole.OWNER, memberCount = 8, + propertyCount = 12, joinedDate = "1/15/2025", isSoleOwner = false, dialog = OrgDetailDialogState.None, @@ -40,6 +41,7 @@ private fun OrgDetailScreenSoleOwnerPreview() = isActiveOrg = true, userRole = OrgRole.OWNER, memberCount = 1, + propertyCount = 1, joinedDate = "3/1/2024", isSoleOwner = true, dialog = OrgDetailDialogState.None, diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailUIState.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailUIState.kt index ae8711df8..99e4cb156 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailUIState.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailUIState.kt @@ -21,6 +21,7 @@ data class OrgDetailUIState( val isActiveOrg: Boolean, val userRole: OrgRole?, val memberCount: Int, + val propertyCount: Int, val joinedDate: String, val isSoleOwner: Boolean, val dialog: OrgDetailDialogState = OrgDetailDialogState.None, @@ -34,6 +35,7 @@ data class OrgDetailUIState( isActiveOrg = false, userRole = null, memberCount = 0, + propertyCount = 0, joinedDate = "", isSoleOwner = false, dialog = OrgDetailDialogState.None, diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModel.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModel.kt index 0fd8dfa53..f7aa7dda2 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModel.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModel.kt @@ -6,6 +6,7 @@ import com.cramsan.edifikana.client.lib.features.window.EdifikanaWindowsEvent import com.cramsan.edifikana.client.lib.managers.AuthManager import com.cramsan.edifikana.client.lib.managers.MembershipManager import com.cramsan.edifikana.client.lib.managers.OrganizationManager +import com.cramsan.edifikana.client.lib.managers.PropertyManager import com.cramsan.edifikana.client.lib.settings.getLastSelectedOrganizationId import com.cramsan.edifikana.lib.model.organization.OrgRole import com.cramsan.edifikana.lib.model.organization.OrganizationId @@ -28,6 +29,7 @@ class OrgDetailViewModel( private val membershipManager: MembershipManager, private val authManager: AuthManager, private val preferencesManager: PreferencesManager, + private val propertyManager: PropertyManager, ) : BaseViewModel( dependencies, OrgDetailUIState.Initial, @@ -45,6 +47,12 @@ class OrgDetailViewModel( val org = organizationManager.getOrganization(orgId).getOrNull() val members = membershipManager.listMembers(orgId).getOrNull() + val propertyCount = + propertyManager + .getPropertyList() + .getOrNull() + ?.count { it.organizationId == orgId } + ?: 0 if (org == null || members == null) { updateUiState { it.copy(isLoading = false) } @@ -63,6 +71,7 @@ class OrgDetailViewModel( isActiveOrg = org.id == activeOrgId, userRole = myMembership?.role, memberCount = members.size, + propertyCount = propertyCount, joinedDate = myMembership?.joinedAt?.toDisplayDate() ?: "", isSoleOwner = isSoleOwner, ) diff --git a/edifikana/front-end/app/src/jvmTest/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModelTest.kt b/edifikana/front-end/app/src/jvmTest/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModelTest.kt index 6c15beb3a..7bedefb21 100644 --- a/edifikana/front-end/app/src/jvmTest/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModelTest.kt +++ b/edifikana/front-end/app/src/jvmTest/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailViewModelTest.kt @@ -7,12 +7,15 @@ import com.cramsan.edifikana.client.lib.features.window.EdifikanaWindowsEvent import com.cramsan.edifikana.client.lib.managers.AuthManager import com.cramsan.edifikana.client.lib.managers.MembershipManager import com.cramsan.edifikana.client.lib.managers.OrganizationManager +import com.cramsan.edifikana.client.lib.managers.PropertyManager import com.cramsan.edifikana.client.lib.models.OrgMemberModel import com.cramsan.edifikana.client.lib.models.Organization +import com.cramsan.edifikana.client.lib.models.PropertyModel import com.cramsan.edifikana.client.lib.settings.EdifikanaSettingKey import com.cramsan.edifikana.lib.model.organization.OrgMemberStatus import com.cramsan.edifikana.lib.model.organization.OrgRole import com.cramsan.edifikana.lib.model.organization.OrganizationId +import com.cramsan.edifikana.lib.model.property.PropertyId import com.cramsan.edifikana.lib.model.user.UserId import com.cramsan.framework.core.UnifiedDispatcherProvider import com.cramsan.framework.core.compose.ApplicationEvent @@ -43,6 +46,7 @@ class OrgDetailViewModelTest : CoroutineTest() { private lateinit var membershipManager: MembershipManager private lateinit var authManager: AuthManager private lateinit var preferencesManager: PreferencesManager + private lateinit var propertyManager: PropertyManager private lateinit var exceptionHandler: CollectorCoroutineExceptionHandler private lateinit var windowEventBus: EventBus private lateinit var applicationEventBus: EventBus @@ -60,6 +64,7 @@ class OrgDetailViewModelTest : CoroutineTest() { membershipManager = mockk() authManager = mockk(relaxed = true) preferencesManager = mockk(relaxed = true) + propertyManager = mockk() every { authManager.activeUser() } returns MutableStateFlow(currentUserId) @@ -75,6 +80,7 @@ class OrgDetailViewModelTest : CoroutineTest() { membershipManager = membershipManager, authManager = authManager, preferencesManager = preferencesManager, + propertyManager = propertyManager, ) } @@ -89,12 +95,23 @@ class OrgDetailViewModelTest : CoroutineTest() { email = "test@example.com", ) + private fun propertyModel(id: String, organizationId: OrganizationId) = + PropertyModel(id = PropertyId(id), name = "Property $id", address = "", organizationId = organizationId) + @Test fun `initialize populates state with org details`() = runCoroutineTest { val org = Organization(id = orgId, name = "My Org", description = "") + val otherOrgId = OrganizationId("org-2") coEvery { organizationManager.getOrganization(orgId) } returns Result.success(org) coEvery { membershipManager.listMembers(orgId) } returns Result.success(listOf(memberModel())) coEvery { preferencesManager.getStringPreference(EdifikanaSettingKey.lastSelectedOrganization) } returns Result.success(orgId.id) + coEvery { propertyManager.getPropertyList() } returns Result.success( + listOf( + propertyModel("prop-1", orgId), + propertyModel("prop-2", orgId), + propertyModel("prop-3", otherOrgId), + ), + ) viewModel.initialize(orgId) @@ -104,6 +121,7 @@ class OrgDetailViewModelTest : CoroutineTest() { assertEquals(true, state.isActiveOrg) assertEquals(OrgRole.OWNER, state.userRole) assertEquals(1, state.memberCount) + assertEquals(2, state.propertyCount) assertTrue(state.isSoleOwner) } @@ -117,17 +135,35 @@ class OrgDetailViewModelTest : CoroutineTest() { coEvery { organizationManager.getOrganization(orgId) } returns Result.success(org) coEvery { membershipManager.listMembers(orgId) } returns Result.success(members) coEvery { preferencesManager.getStringPreference(EdifikanaSettingKey.lastSelectedOrganization) } returns Result.success(null) + coEvery { propertyManager.getPropertyList() } returns Result.success(emptyList()) viewModel.initialize(orgId) assertFalse(viewModel.uiState.value.isSoleOwner) } + @Test + fun `initialize sets propertyCount to 0 when property fetch fails`() = runCoroutineTest { + val org = Organization(id = orgId, name = "My Org", description = "") + coEvery { organizationManager.getOrganization(orgId) } returns Result.success(org) + coEvery { membershipManager.listMembers(orgId) } returns Result.success(listOf(memberModel())) + coEvery { preferencesManager.getStringPreference(EdifikanaSettingKey.lastSelectedOrganization) } returns Result.success(orgId.id) + coEvery { propertyManager.getPropertyList() } returns Result.failure(RuntimeException("error")) + + viewModel.initialize(orgId) + + val state = viewModel.uiState.value + assertFalse(state.isLoading) + assertEquals(0, state.propertyCount) + assertEquals("My Org", state.orgName) + } + @Test fun `initialize shows snackbar on org load failure`() = runCoroutineTest { coEvery { organizationManager.getOrganization(orgId) } returns Result.failure(RuntimeException("error")) coEvery { membershipManager.listMembers(orgId) } returns Result.success(emptyList()) coEvery { preferencesManager.getStringPreference(EdifikanaSettingKey.lastSelectedOrganization) } returns Result.success(null) + coEvery { propertyManager.getPropertyList() } returns Result.success(emptyList()) turbineScope { val turbine = windowEventBus.events.testIn(backgroundScope) From 8e8d8af1ea04a585adc6780e1b8880dc659e564e Mon Sep 17 00:00:00 2001 From: Cramsan Date: Wed, 22 Jul 2026 18:30:18 -0700 Subject: [PATCH 2/4] [EDIFIKANA] Fix OrgDetailScreen mock fidelity: cards, badge, typography (#542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OrgDetailScreen's cards, "Active" status, and typography had drifted from organization-detail.html: bare Card() with no shape/border/elevation, a plain-text "Active" label instead of a badge, headlineSmall/bodyMedium defaults instead of the mock's declared sizes/weights, and a bare Transfer Ownership row with no leading icon. - Apply Shapes.pill/outlineVariant border/Elevation.resting to all three cards, matching the pattern already established in MyOrganizationsScreen.OrgCard, plus an explicit surfaceContainerLowest container color so cards render white instead of tonal-elevation gray. - Add StatusColors (success/warning container + on-container, light+dark) to ui-components/theme — no equivalent semantic color existed anywhere in the Kotlin theme for the mock's green/amber badge tokens. - Render "Active" as a real pill Surface instead of plain colored text. - Match org-name and info-row value typography to the mock's declared sizes/weights (20sp/600, 16sp/500) via local style overrides. - Add IconBadge (wrench, warning colors) as the Transfer Ownership row's leading element, matching the mock's list-item icon. --- .../OrgDetailScreenPreview_Desktop.png | 4 +- .../OrgDetailScreenPreview_Phone.png | 4 +- .../OrgDetailScreenPreview_Tablet.png | 4 +- .../OrgDetailScreenSoleOwnerPreview.png | 4 +- .../orgdetail/OrgDetailScreen.kt | 96 ++++++++++++++++--- .../edifikana/client/ui/theme/StatusColors.kt | 47 +++++++++ 6 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/StatusColors.kt diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png index 088668813..c084d4bdd 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83c937931bf6b37f9f1c80f49338c633afb5550833101f176b5802c2c4dade46 -size 110434 +oid sha256:ee4f19dea5239d5f630ae28930ae9b25008657e284b8614336b6d75ee9624267 +size 147265 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png index 4b9c3eaad..0286f5a7f 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:922d268d06984308aeeebdd958726063143eb64967503b3f48e605a6c291263d -size 79838 +oid sha256:0b38c2c112d16a012065816cbe920050174aa723262f62c355bdf5e3517ebf39 +size 106740 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png index 5a1446ded..b4ff24f30 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8f1a330f0f753d893c23650739933bacbc14735698f7bc8fa172919a7ca048d -size 97754 +oid sha256:6b356aba6c798092530a8f6f2525f0280fbb0fa57e2ba9bdad2b7b2223026822 +size 130483 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png index f6de80f56..7da3c2b29 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ede9f4f6285095ecfecd89b417d5e868c8d471f8917c1c65ee7f07ef5f2937b -size 77953 +oid sha256:65d0ef3432789378a073229face6109b42383d991e6a9783e87f0c1fd119b58e +size 105884 diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt index 86a08b76c..2c724135a 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt @@ -1,21 +1,26 @@ package com.cramsan.edifikana.client.lib.features.settings.organizations.orgdetail +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.Build import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -23,11 +28,18 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import com.cramsan.edifikana.client.lib.features.settings.SettingsDestination import com.cramsan.edifikana.client.ui.components.EdifikanaTopBar +import com.cramsan.edifikana.client.ui.components.IconBadge +import com.cramsan.edifikana.client.ui.theme.Elevation +import com.cramsan.edifikana.client.ui.theme.Shapes import com.cramsan.edifikana.client.ui.theme.Spacing +import com.cramsan.edifikana.client.ui.theme.StatusColors import com.cramsan.edifikana.lib.model.organization.OrgRole import com.cramsan.framework.core.compose.ui.ObserveViewModelEvents import com.cramsan.ui.components.LoadingAnimationOverlay @@ -160,7 +172,18 @@ private fun OrgHeaderCard( isActive: Boolean, modifier: Modifier = Modifier, ) { - Card(modifier = modifier.fillMaxWidth()) { + Card( + modifier = modifier.fillMaxWidth(), + shape = Shapes.pill, + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant), + elevation = + CardDefaults.cardElevation( + defaultElevation = Elevation.resting, + hoveredElevation = Elevation.hovered, + pressedElevation = Elevation.pressed, + ), + ) { Row( modifier = Modifier @@ -170,13 +193,32 @@ private fun OrgHeaderCard( horizontalArrangement = Arrangement.spacedBy(Spacing.lg), ) { Column { - Text(text = orgName, style = MaterialTheme.typography.headlineSmall) + Text( + text = orgName, + style = + MaterialTheme.typography.headlineSmall.copy( + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold, + ), + ) if (isActive) { - Text( - text = "Active", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.primary, - ) + Surface( + color = StatusColors.successContainer, + contentColor = StatusColors.onSuccessContainer, + shape = RoundedCornerShape(percent = 50), + modifier = Modifier.padding(top = Spacing.xs), + ) { + Text( + text = "Active", + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.SemiBold, + modifier = + Modifier.padding( + horizontal = Spacing.md, + vertical = Spacing.xs, + ), + ) + } } } } @@ -191,7 +233,18 @@ private fun InfoCard( joinedDate: String, modifier: Modifier = Modifier, ) { - Card(modifier = modifier.fillMaxWidth()) { + Card( + modifier = modifier.fillMaxWidth(), + shape = Shapes.pill, + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant), + elevation = + CardDefaults.cardElevation( + defaultElevation = Elevation.resting, + hoveredElevation = Elevation.hovered, + pressedElevation = Elevation.pressed, + ), + ) { Column(modifier = Modifier.padding(Spacing.lg)) { InfoRow( label = "Your Role", @@ -222,7 +275,12 @@ private fun InfoRow( style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) - Text(text = value, style = MaterialTheme.typography.bodyMedium) + Text( + text = value, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + ) } } @@ -232,10 +290,17 @@ private fun TransferOwnershipRow( modifier: Modifier = Modifier, ) { Card( - modifier = - modifier - .fillMaxWidth(), + modifier = modifier.fillMaxWidth(), onClick = onClick, + shape = Shapes.pill, + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant), + elevation = + CardDefaults.cardElevation( + defaultElevation = Elevation.resting, + hoveredElevation = Elevation.hovered, + pressedElevation = Elevation.pressed, + ), ) { Row( modifier = @@ -243,7 +308,14 @@ private fun TransferOwnershipRow( .fillMaxWidth() .padding(Spacing.lg), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(Spacing.md), ) { + IconBadge( + icon = Icons.Filled.Build, + contentDescription = null, + backgroundColor = StatusColors.warningContainer, + tint = StatusColors.onWarningContainer, + ) Column(modifier = Modifier.weight(1f)) { Text(text = "Transfer Ownership", style = MaterialTheme.typography.bodyLarge) Text( diff --git a/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/StatusColors.kt b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/StatusColors.kt new file mode 100644 index 000000000..a85bc0627 --- /dev/null +++ b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/StatusColors.kt @@ -0,0 +1,47 @@ +@file:Suppress("MagicNumber") + +package com.cramsan.edifikana.client.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color + +/** + * Semantic status colors (success, warning) not represented in Material3's default `ColorScheme`. + */ +object StatusColors { + /** Background for a positive/active state, e.g. an "Active" badge. */ + val successContainer: Color + @Composable + @ReadOnlyComposable + get() = if (isSystemInDarkTheme()) successContainerDark else successContainerLight + + /** Content color to pair with [successContainer]. */ + val onSuccessContainer: Color + @Composable + @ReadOnlyComposable + get() = if (isSystemInDarkTheme()) onSuccessContainerDark else onSuccessContainerLight + + /** Background for a cautionary state, e.g. an ownership-transfer action icon. */ + val warningContainer: Color + @Composable + @ReadOnlyComposable + get() = if (isSystemInDarkTheme()) warningContainerDark else warningContainerLight + + /** Content color to pair with [warningContainer]. */ + val onWarningContainer: Color + @Composable + @ReadOnlyComposable + get() = if (isSystemInDarkTheme()) onWarningContainerDark else onWarningContainerLight +} + +private val successContainerLight = Color(0xFFDCFCE7) +private val onSuccessContainerLight = Color(0xFF16A34A) +private val successContainerDark = Color(0xFF14532D) +private val onSuccessContainerDark = Color(0xFF86EFAC) + +private val warningContainerLight = Color(0xFFFEF3C7) +private val onWarningContainerLight = Color(0xFFF59E0B) +private val warningContainerDark = Color(0xFF78350F) +private val onWarningContainerDark = Color(0xFFFCD34D) From 09631eaa9746001ac6d251af6b3e1cf9db73e8d2 Mon Sep 17 00:00:00 2001 From: Cramsan Date: Wed, 22 Jul 2026 19:28:44 -0700 Subject: [PATCH 3/4] [EDIFIKANA] Extract StatusPillBadge, typography/shape tokens (#542) review-ui flagged three P1s on the OrgDetailScreen mock-fidelity fix: a hardcoded fontSize/fontWeight literal for the org name, a hardcoded RoundedCornerShape(percent = 50) badge shape literal, and a duplicated badge composable (structurally identical to MyOrganizationsScreen's "Current" badge). - Add Shapes.badgePill and Typography.cardTitle tokens to ui-components/theme, following the existing Shapes/Elevation/Spacing token-object convention. - Extract StatusPillBadge (ui-components/components) and use it from both OrgDetailScreen's "Active" badge and MyOrganizationsScreen's "Current" badge, removing the duplication instead of just fixing it in one place. --- .../MyOrganizationsScreenPreview_Desktop.png | 4 +- .../MyOrganizationsScreenPreview_Phone.png | 4 +- .../MyOrganizationsScreenPreview_Tablet.png | 4 +- .../OrgDetailScreenPreview_Desktop.png | 4 +- .../OrgDetailScreenPreview_Phone.png | 4 +- .../OrgDetailScreenPreview_Tablet.png | 4 +- .../OrgDetailScreenSoleOwnerPreview.png | 4 +- .../myorganizations/MyOrganizationsScreen.kt | 24 ++-------- .../orgdetail/OrgDetailScreen.kt | 30 +++--------- .../client/ui/components/StatusPillBadge.kt | 48 +++++++++++++++++++ .../edifikana/client/ui/theme/Shapes.kt | 3 ++ .../edifikana/client/ui/theme/Typography.kt | 20 ++++++++ 12 files changed, 97 insertions(+), 56 deletions(-) create mode 100644 edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/components/StatusPillBadge.kt create mode 100644 edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Typography.kt diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png index beb6b64ce..7468f7fd9 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9746b2cc99349cea732e54678d922c4d7638a0277610cd201effccf89a3667f -size 142606 +oid sha256:3d0bec6a739699b026fb3de100a1e26f61922fd3aaebd30869cf803c72125965 +size 142948 diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png index 908c62402..1e6aef904 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98f849f8e9d14147433569539a7ebcb01312bf548dc9cc77aa0fec6a15c54fff -size 104011 +oid sha256:cb603ac5ba6bae2cda914737b2dccda35a9ee819defef4b88d8a535aac0d77ca +size 104462 diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png index 2fcd599bb..d5d994f89 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8fddb807796cbc7693a38e0d3976c1e79e37e37ce562a2df977b1a3956f6062 -size 126283 +oid sha256:4b17f36b4ed01c7b796ea3d71d3ad7f7a1a30ce320ca5c604b2dee1127a21644 +size 126667 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png index c084d4bdd..290c0f085 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee4f19dea5239d5f630ae28930ae9b25008657e284b8614336b6d75ee9624267 -size 147265 +oid sha256:db326245795ae9979639f12a02ba98a33a3b95f44145326ec9c04b439096ebfb +size 146684 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png index 0286f5a7f..f57ffa8c0 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b38c2c112d16a012065816cbe920050174aa723262f62c355bdf5e3517ebf39 -size 106740 +oid sha256:9ff43576ec1d1e12ce3013070a4593dbf0fdb240125a27c99d9e4aff6e98f331 +size 106768 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png index b4ff24f30..0cc4dfb8f 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b356aba6c798092530a8f6f2525f0280fbb0fa57e2ba9bdad2b7b2223026822 -size 130483 +oid sha256:ae3b0c04e8be53d0a54bb5047f493cab568949264989a6a0c3e1e713fe9e8a7f +size 129831 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png index 7da3c2b29..404642ab4 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65d0ef3432789378a073229face6109b42383d991e6a9783e87f0c1fd119b58e -size 105884 +oid sha256:ace409602933ec8a32523f972bce1f659af0e2bdb7e5ac6ee83de1503e932212 +size 105890 diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/myorganizations/MyOrganizationsScreen.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/myorganizations/MyOrganizationsScreen.kt index cc765f366..2f12c2866 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/myorganizations/MyOrganizationsScreen.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/myorganizations/MyOrganizationsScreen.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Business @@ -18,7 +17,6 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -34,6 +32,7 @@ import androidx.lifecycle.compose.LifecycleEventEffect import com.cramsan.edifikana.client.ui.components.EdifikanaPrimaryButton import com.cramsan.edifikana.client.ui.components.EdifikanaTopBar import com.cramsan.edifikana.client.ui.components.IconBadge +import com.cramsan.edifikana.client.ui.components.StatusPillBadge import com.cramsan.edifikana.client.ui.theme.Elevation import com.cramsan.edifikana.client.ui.theme.Shapes import com.cramsan.edifikana.client.ui.theme.Spacing @@ -187,24 +186,11 @@ private fun OrgCard( modifier = Modifier.weight(1f, fill = false), ) if (org.isActive) { - Surface( - color = MaterialTheme.colorScheme.primaryContainer, + StatusPillBadge( + text = "Current", + containerColor = MaterialTheme.colorScheme.primaryContainer, contentColor = MaterialTheme.colorScheme.primary, - shape = RoundedCornerShape(percent = 50), - ) { - Text( - text = "Current", - style = MaterialTheme.typography.labelSmall, - fontWeight = FontWeight.SemiBold, - maxLines = 1, - softWrap = false, - modifier = - Modifier.padding( - horizontal = Spacing.md, - vertical = Spacing.xs, - ), - ) - } + ) } } Text( diff --git a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt index 2c724135a..07a00c8f6 100644 --- a/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt +++ b/edifikana/front-end/app/src/commonMain/kotlin/com/cramsan/edifikana/client/lib/features/settings/organizations/orgdetail/OrgDetailScreen.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Build @@ -20,7 +19,6 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -30,16 +28,17 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import com.cramsan.edifikana.client.lib.features.settings.SettingsDestination import com.cramsan.edifikana.client.ui.components.EdifikanaTopBar import com.cramsan.edifikana.client.ui.components.IconBadge +import com.cramsan.edifikana.client.ui.components.StatusPillBadge import com.cramsan.edifikana.client.ui.theme.Elevation import com.cramsan.edifikana.client.ui.theme.Shapes import com.cramsan.edifikana.client.ui.theme.Spacing import com.cramsan.edifikana.client.ui.theme.StatusColors +import com.cramsan.edifikana.client.ui.theme.Typography import com.cramsan.edifikana.lib.model.organization.OrgRole import com.cramsan.framework.core.compose.ui.ObserveViewModelEvents import com.cramsan.ui.components.LoadingAnimationOverlay @@ -195,30 +194,15 @@ private fun OrgHeaderCard( Column { Text( text = orgName, - style = - MaterialTheme.typography.headlineSmall.copy( - fontSize = 20.sp, - fontWeight = FontWeight.SemiBold, - ), + style = Typography.cardTitle, ) if (isActive) { - Surface( - color = StatusColors.successContainer, + StatusPillBadge( + text = "Active", + containerColor = StatusColors.successContainer, contentColor = StatusColors.onSuccessContainer, - shape = RoundedCornerShape(percent = 50), modifier = Modifier.padding(top = Spacing.xs), - ) { - Text( - text = "Active", - style = MaterialTheme.typography.labelSmall, - fontWeight = FontWeight.SemiBold, - modifier = - Modifier.padding( - horizontal = Spacing.md, - vertical = Spacing.xs, - ), - ) - } + ) } } } diff --git a/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/components/StatusPillBadge.kt b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/components/StatusPillBadge.kt new file mode 100644 index 000000000..61ac4c989 --- /dev/null +++ b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/components/StatusPillBadge.kt @@ -0,0 +1,48 @@ +package com.cramsan.edifikana.client.ui.components + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import com.cramsan.edifikana.client.ui.theme.Shapes +import com.cramsan.edifikana.client.ui.theme.Spacing + +/** + * Compact pill-shaped status badge, e.g. "Active" or "Current". + * + * @param text Label to display within the badge. + * @param containerColor Background color of the badge. + * @param contentColor Text color of the badge. + * @param modifier Modifier for the badge container. + */ +@Composable +fun StatusPillBadge( + text: String, + containerColor: Color, + contentColor: Color, + modifier: Modifier = Modifier, +) { + Surface( + modifier = modifier, + color = containerColor, + contentColor = contentColor, + shape = Shapes.badgePill, + ) { + Text( + text = text, + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + softWrap = false, + modifier = + Modifier.padding( + horizontal = Spacing.md, + vertical = Spacing.xs, + ), + ) + } +} diff --git a/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Shapes.kt b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Shapes.kt index 2837b3de1..7805722be 100644 --- a/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Shapes.kt +++ b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Shapes.kt @@ -10,4 +10,7 @@ import androidx.compose.ui.unit.dp object Shapes { /** Fully rounded "pill" shape used for buttons, text fields, and cards. */ val pill: Shape = RoundedCornerShape(25.dp) + + /** Fully rounded, height-relative "pill" shape used for compact status badges/chips. */ + val badgePill: Shape = RoundedCornerShape(percent = 50) } diff --git a/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Typography.kt b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Typography.kt new file mode 100644 index 000000000..6ac26ebc7 --- /dev/null +++ b/edifikana/front-end/ui-components/src/commonMain/kotlin/com/cramsan/edifikana/client/ui/theme/Typography.kt @@ -0,0 +1,20 @@ +@file:Suppress("MagicNumber") + +package com.cramsan.edifikana.client.ui.theme + +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +/** + * Custom typography tokens not covered by Material3's default type scale. + */ +object Typography { + /** Emphasized card/section title, e.g. an organization name header. */ + val cardTitle: TextStyle = + TextStyle( + fontSize = 20.sp, + lineHeight = 28.sp, + fontWeight = FontWeight.SemiBold, + ) +} From 9d31e692af22397a46f13806e2f9520fdee85053 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Jul 2026 20:13:37 -0700 Subject: [PATCH 4/4] [ignore-commit] Committing CI changes --- .../MyOrganizationsScreenPreview_Desktop.png | 4 ++-- .../MyOrganizationsScreenPreview_Phone.png | 4 ++-- .../MyOrganizationsScreenPreview_Tablet.png | 4 ++-- .../OrgDetailScreenPreview_Desktop.png | 4 ++-- .../OrgDetailScreenPreview_Phone.png | 4 ++-- .../OrgDetailScreenPreview_Tablet.png | 4 ++-- .../OrgDetailScreenSoleOwnerPreview.png | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png index 7468f7fd9..beb6b64ce 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d0bec6a739699b026fb3de100a1e26f61922fd3aaebd30869cf803c72125965 -size 142948 +oid sha256:e9746b2cc99349cea732e54678d922c4d7638a0277610cd201effccf89a3667f +size 142606 diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png index 1e6aef904..908c62402 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb603ac5ba6bae2cda914737b2dccda35a9ee819defef4b88d8a535aac0d77ca -size 104462 +oid sha256:98f849f8e9d14147433569539a7ebcb01312bf548dc9cc77aa0fec6a15c54fff +size 104011 diff --git a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png index d5d994f89..2fcd599bb 100644 --- a/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/MyOrganizationsScreenPreviewKt.myorganizations.organizations.settings.features.lib.client.edifikana.cramsan.com/MyOrganizationsScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b17f36b4ed01c7b796ea3d71d3ad7f7a1a30ce320ca5c604b2dee1127a21644 -size 126667 +oid sha256:b8fddb807796cbc7693a38e0d3976c1e79e37e37ce562a2df977b1a3956f6062 +size 126283 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png index 290c0f085..edf39bb08 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db326245795ae9979639f12a02ba98a33a3b95f44145326ec9c04b439096ebfb -size 146684 +oid sha256:c561e275833943648178767a99e30053a6aba787e3ca6618a3d9121ce0c5a8a4 +size 146606 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png index f57ffa8c0..52b5f311d 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Phone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ff43576ec1d1e12ce3013070a4593dbf0fdb240125a27c99d9e4aff6e98f331 -size 106768 +oid sha256:479bd1dbb4c1b837160551c4e570126b03b17acd55bf1ed30c5f4353f1bac3e6 +size 106711 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png index 0cc4dfb8f..0bfba78f7 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenPreview_Tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae3b0c04e8be53d0a54bb5047f493cab568949264989a6a0c3e1e713fe9e8a7f -size 129831 +oid sha256:5c6c76dfcb4b0a04e6d84d9a7a275adb38d3183a0c3e97e3becd12dc671bee92 +size 129724 diff --git a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png index 404642ab4..51d685ec0 100644 --- a/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png +++ b/edifikana/front-end/app/screenshots/OrgDetailScreenPreviewKt.orgdetail.organizations.settings.features.lib.client.edifikana.cramsan.com/OrgDetailScreenSoleOwnerPreview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ace409602933ec8a32523f972bce1f659af0e2bdb7e5ac6ee83de1503e932212 -size 105890 +oid sha256:7042aab96830a2b82f583bff03be517966bae108669bb66580134a8a8808c9d9 +size 105945