diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt
new file mode 100644
index 000000000..e2f973ad9
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardDefaults.kt
@@ -0,0 +1,44 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
+
+@Immutable
+data class ContactCardColors(
+ val waveBackground: Color,
+ val background: Color,
+)
+
+object ContactCardDefaults {
+ @Composable
+ fun colors(
+ waveBackground: Color = Color.Unspecified,
+ background: Color = Color.Unspecified,
+ ): ContactCardColors {
+ val defaultWave = MaterialTheme.colorScheme.surfaceContainerLowest
+ val defaultBackground = MaterialTheme.colorScheme.primaryContainer
+ return ContactCardColors(
+ waveBackground = waveBackground.takeOrElse { defaultWave },
+ background = background.takeOrElse { defaultBackground },
+ )
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt
new file mode 100644
index 000000000..526b3b58d
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/ContactCardTopBarState.kt
@@ -0,0 +1,23 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard
+
+sealed interface ContactCardTopBarState {
+ data class Editor(val onCancel: () -> Unit, val onSave: () -> Unit) : ContactCardTopBarState
+ data class Preview(val onClose: () -> Unit, val onMore: () -> Unit) : ContactCardTopBarState
+ data class Default(val onBack: () -> Unit) : ContactCardTopBarState
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt
new file mode 100644
index 000000000..b8b235469
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/BottomSheetAction.kt
@@ -0,0 +1,92 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.infomaniak.core.ui.compose.margin.Margin
+
+@Composable
+internal fun BottomSheetAction(
+ icon: ImageVector,
+ label: String,
+ onClick: () -> Unit,
+) {
+ TextButton(
+ onClick = onClick,
+ shape = RectangleShape,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = Margin.Mini),
+ contentPadding = PaddingValues(Margin.Small),
+ colors = ButtonDefaults.textButtonColors(
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ )
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.size(24.dp),
+ )
+ Spacer(Modifier.width(Margin.Medium))
+ Text(
+ text = label,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
+ }
+}
+
+@Preview(name = "BottomSheetAction")
+@Composable
+private fun BottomSheetActionPreview() {
+ MaterialTheme {
+ Surface {
+ BottomSheetAction(
+ icon = Icons.Default.Edit,
+ label = "Edit",
+ onClick = {},
+ )
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt
new file mode 100644
index 000000000..a6bc9d667
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardConstants.kt
@@ -0,0 +1,21 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.ui.unit.dp
+
+internal val CardCornerRadius = 12.dp
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt
new file mode 100644
index 000000000..1efc7e028
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactCardTopBar.kt
@@ -0,0 +1,54 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import com.infomaniak.core.ui.compose.contactcard.R
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+internal fun ContactCardTopBar(
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+ containerColor: Color = Color.Transparent,
+) {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = stringResource(R.string.contactCardTitle),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ },
+ navigationIcon = navigationIcon,
+ actions = actions,
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = containerColor,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ navigationIconContentColor = MaterialTheme.colorScheme.primary,
+ actionIconContentColor = MaterialTheme.colorScheme.primary,
+ ),
+ )
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt
new file mode 100644
index 000000000..534f3f937
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/ContactInfoRows.kt
@@ -0,0 +1,90 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
+import com.infomaniak.core.auth.models.user.Card
+import com.infomaniak.core.auth.models.user.CardLinkType
+import com.infomaniak.core.ui.compose.contactcard.R
+import com.infomaniak.core.ui.compose.margin.Margin
+
+@Composable
+internal fun ContactInfoRows(card: Card, modifier: Modifier = Modifier) {
+ val rows = buildList {
+ card.company?.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.company) to it) }
+ card.phone.takeIf { it.isNotBlank() }?.let { add(stringResource(R.string.phone) to it) }
+ card.links.orEmpty().firstOrNull { it.type == CardLinkType.Website && it.url.isNotBlank() }?.let {
+ add(stringResource(R.string.webSite) to it.url)
+ }
+ }
+ Column(modifier = modifier) {
+ rows.forEachIndexed { index, (label, value) ->
+ if (index > 0) {
+ HorizontalDivider(
+ thickness = 0.5.dp,
+ color = MaterialTheme.colorScheme.outlineVariant,
+ )
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = Margin.Small),
+ horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = label,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ Text(
+ text = value,
+ color = MaterialTheme.colorScheme.primary,
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.SemiBold,
+ )
+ }
+ }
+ }
+}
+
+@Preview(name = "ContactInfoRows")
+@Composable
+private fun ContactInfoRowsPreview(
+ @PreviewParameter(ContactPreviewProvider::class) contactData: PreviewContactData,
+) {
+ MaterialTheme {
+ Surface {
+ ContactInfoRows(card = contactData.card)
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt
new file mode 100644
index 000000000..60c482b54
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/DefaultContactCardDialog.kt
@@ -0,0 +1,82 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.infomaniak.core.ui.compose.contactcard.R
+import com.infomaniak.core.common.R as RCore
+
+@Composable
+internal fun DefaultContactCardDialog(
+ titleRes: Int,
+ descriptionRes: Int,
+ confirmButtonRes: Int,
+ onConfirm: () -> Unit,
+ onDismiss: () -> Unit,
+ dismissButtonRes: Int? = null,
+) {
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ confirmButton = {
+ TextButton(onClick = onConfirm) {
+ Text(text = stringResource(confirmButtonRes))
+ }
+ },
+ dismissButton = dismissButtonRes?.let {
+ {
+ TextButton(onClick = onDismiss) {
+ Text(text = stringResource(it))
+ }
+ }
+ },
+ title = { Text(text = stringResource(titleRes)) },
+ text = { Text(text = stringResource(descriptionRes)) },
+ )
+}
+
+@Composable
+internal fun DefaultValidationErrorDialog(
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit,
+) {
+ DefaultContactCardDialog(
+ titleRes = R.string.alertTitle,
+ descriptionRes = R.string.alertDescription,
+ confirmButtonRes = android.R.string.ok,
+ onConfirm = onConfirm,
+ onDismiss = onDismiss,
+ )
+}
+
+@Composable
+internal fun DefaultDeleteConfirmationDialog(
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit,
+) {
+ DefaultContactCardDialog(
+ titleRes = R.string.deleteAlertTitle,
+ descriptionRes = R.string.deleteAlertDescription,
+ confirmButtonRes = R.string.deleteButton,
+ onConfirm = onConfirm,
+ onDismiss = onDismiss,
+ dismissButtonRes = RCore.string.buttonCancel,
+ )
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt
new file mode 100644
index 000000000..064501db5
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorField.kt
@@ -0,0 +1,74 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.tooling.preview.Preview
+
+@Composable
+internal fun EditorField(
+ value: String,
+ placeholder: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier.fillMaxWidth(),
+ keyboardType: KeyboardType = KeyboardType.Text,
+) {
+ TextField(
+ value = value,
+ onValueChange = onValueChange,
+ modifier = modifier,
+ placeholder = {
+ Text(text = placeholder)
+ },
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(keyboardType = keyboardType),
+ colors = TextFieldDefaults.colors(
+ focusedContainerColor = Color.Transparent,
+ unfocusedContainerColor = Color.Transparent,
+ disabledContainerColor = Color.Transparent,
+ errorContainerColor = Color.Transparent,
+ focusedIndicatorColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ disabledIndicatorColor = Color.Transparent,
+ errorIndicatorColor = Color.Transparent,
+ ),
+ )
+}
+
+@Preview(name = "EditorField")
+@Composable
+private fun EditorFieldPreview() {
+ MaterialTheme {
+ Surface {
+ EditorField(
+ value = "",
+ placeholder = "Placeholder",
+ onValueChange = {},
+ )
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt
new file mode 100644
index 000000000..5689bf7d5
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/EditorTopBar.kt
@@ -0,0 +1,64 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.infomaniak.core.common.R as RCore
+
+@Composable
+internal fun EditorTopBar(
+ onCancel: () -> Unit,
+ onSave: () -> Unit,
+) {
+ ContactCardTopBar(
+ navigationIcon = {
+ TextButton(onClick = onCancel) {
+ Text(
+ text = stringResource(RCore.string.buttonCancel),
+ color = MaterialTheme.colorScheme.primary,
+ )
+ }
+ },
+ actions = {
+ TextButton(onClick = onSave) {
+ Text(
+ text = stringResource(RCore.string.buttonSave),
+ color = MaterialTheme.colorScheme.primary,
+ )
+ }
+ },
+ )
+}
+
+@Preview(name = "EditorTopBar")
+@Composable
+private fun EditorTopBarPreview() {
+ MaterialTheme {
+ Surface {
+ EditorTopBar(
+ onCancel = {},
+ onSave = {},
+ )
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt
new file mode 100644
index 000000000..20b31cfc6
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/FieldDivider.kt
@@ -0,0 +1,44 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.infomaniak.core.ui.compose.margin.Margin
+
+@Composable
+internal fun FieldDivider() {
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = Margin.Medium),
+ color = MaterialTheme.colorScheme.outlineVariant,
+ )
+}
+
+@Preview(name = "FieldDivider")
+@Composable
+private fun FieldDividerPreview() {
+ MaterialTheme {
+ Surface {
+ FieldDivider()
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt
new file mode 100644
index 000000000..2a8f7d1ad
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/LoadingContent.kt
@@ -0,0 +1,44 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+
+@Composable
+internal fun LoadingContent() {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ CircularProgressIndicator()
+ }
+}
+
+@Preview(name = "LoadingContent")
+@Composable
+private fun LoadingContentPreview() {
+ MaterialTheme {
+ Surface {
+ LoadingContent()
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt
new file mode 100644
index 000000000..bfac309b8
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewData.kt
@@ -0,0 +1,65 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import com.infomaniak.core.auth.models.user.Card
+import com.infomaniak.core.auth.models.user.CardLink
+import com.infomaniak.core.auth.models.user.CardLinkType
+import com.infomaniak.core.auth.models.user.User
+import com.infomaniak.core.auth.models.user.preferences.OrganizationPreference
+import com.infomaniak.core.auth.models.user.preferences.Preferences
+
+public data class PreviewContactData(
+ val user: User,
+ val card: Card,
+)
+
+public class ContactPreviewProvider : PreviewParameterProvider {
+ override val values: Sequence = sequenceOf(
+ PreviewContactData(
+ user = User(
+ id = 42,
+ displayName = "Alice Doe",
+ firstname = "Alice",
+ lastname = "Doe",
+ email = "alice.doe@example.com",
+ avatar = "https://example.com/avatar.png",
+ card = null,
+ login = "alice.doe",
+ isStaff = false,
+ preferences = Preferences(
+ security = null,
+ organizationPreference = OrganizationPreference(currentOrganizationId = 0),
+ ),
+ ),
+ card = Card(
+ firstName = "Alice",
+ lastName = "Doe",
+ email = "alice.doe@example.com",
+ phone = "+41 79 123 45 67",
+ company = "Infomaniak",
+ avatarUrl = "https://example.com/avatar.png",
+ links = listOf(
+ CardLink(CardLinkType.LinkedIn, "https://linkedin.com/in/alicedoe"),
+ CardLink(CardLinkType.Website, "https://example.com"),
+ CardLink(CardLinkType.Other, "https://blog.example.com"),
+ ),
+ ),
+ )
+ )
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt
new file mode 100644
index 000000000..f04b24fd4
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/PreviewTopBar.kt
@@ -0,0 +1,70 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.infomaniak.core.ui.compose.contactcard.R
+import com.infomaniak.core.common.R as RCore
+
+@Composable
+internal fun PreviewTopBar(
+ onClose: () -> Unit,
+ onMore: () -> Unit,
+) {
+ ContactCardTopBar(
+ navigationIcon = {
+ IconButton(onClick = onClose) {
+ Icon(
+ imageVector = Icons.Filled.Close,
+ contentDescription = stringResource(RCore.string.buttonClose),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ }
+ },
+ actions = {
+ IconButton(onClick = onMore) {
+ Icon(
+ imageVector = Icons.Filled.MoreVert,
+ contentDescription = stringResource(R.string.buttonMore),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ }
+ },
+ )
+}
+
+@Preview(name = "PreviewTopBar")
+@Composable
+private fun PreviewTopBarPreview() {
+ MaterialTheme {
+ Surface {
+ PreviewTopBar(
+ onClose = {},
+ onMore = {},
+ )
+ }
+ }
+}
diff --git a/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt
new file mode 100644
index 000000000..46fb5322a
--- /dev/null
+++ b/Ui/Compose/ContactCard/src/main/kotlin/com/infomaniak/core/ui/compose/contactcard/component/SectionCard.kt
@@ -0,0 +1,69 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.ui.compose.contactcard.component
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+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.tooling.preview.Preview
+import com.infomaniak.core.ui.compose.margin.Margin
+
+@Composable
+internal fun SectionCard(
+ title: String,
+ content: @Composable () -> Unit,
+) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.padding(
+ start = Margin.Medium + Margin.Mini,
+ end = Margin.Medium,
+ bottom = Margin.Mini,
+ ),
+ )
+ Surface(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = Margin.Medium),
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(CardCornerRadius),
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) { content() }
+ }
+ }
+}
+
+@Preview(name = "SectionCard")
+@Composable
+private fun SectionCardPreview() {
+ MaterialTheme {
+ Surface {
+ SectionCard(title = "Section Title") {
+ Text("Content")
+ }
+ }
+ }
+}