modification local CRUD

This commit is contained in:
2025-12-15 17:06:30 +01:00
parent 70fa5292e1
commit 14efe45b69
17 changed files with 597 additions and 12 deletions

View File

@@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
kotlin("kapt")
}
android {
@@ -41,6 +42,8 @@ android {
}
}
val roomVersion = "2.8.4"
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
@@ -50,6 +53,11 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation("com.squareup.retrofit2:retrofit:3.0.0")
implementation("com.squareup.retrofit2:converter-gson:3.0.0")
implementation("androidx.room:room-runtime:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View File

@@ -25,6 +25,12 @@
<activity android:name=".ui.screens.ListeClientsActivity"
android:exported="true"/>
<activity android:name=".ui.screens.AjoutClientActivity"
android:exported="true"/>
<activity android:name=".ui.screens.ModifClientActivity"
android:exported="true"/>
<activity android:name=".ui.screens.SuppClientActivity"
android:exported="true"/>
</application>
</manifest>

View File

@@ -15,10 +15,14 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sio.gestionclients.ui.screens.AjoutClientActivity
import com.sio.gestionclients.ui.screens.ListeClientsActivity
import com.sio.gestionclients.ui.screens.ModifClientActivity
import com.sio.gestionclients.ui.screens.SuppClientActivity
import com.sio.gestionclients.ui.theme.GestionClientsTheme
import kotlin.system.exitProcess
@@ -32,6 +36,7 @@ class MainActivity : ComponentActivity() {
@Composable
fun MenuScreen() {
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
@@ -42,16 +47,36 @@ fun MenuScreen() {
Text("Gestion de clients", fontSize = 28.sp)
Button(onClick = {
.startActivity(Intent(context, ListeClientsActivity::class.java))
context.startActivity(Intent(context, ListeClientsActivity::class.java))
}) {
Text("Liste des clients")
}
Button(onClick = {
context.startActivity(Intent(context, AjoutClientActivity::class.java))
}) {
Text("Ajouter un client")
}
Button(onClick = {
context.startActivity(Intent(context, SuppClientActivity::class.java))
}) {
Text("Supprimer un client")
}
Button(onClick = {
context.startActivity(Intent(context, ModifClientActivity::class.java))
}) {
Text("Modifier un client")
}
Button(onClick = {}) {
Text("Synchronisation serveur")
}
Button(onClick = { exitProcess(0) }) { }
Button(onClick = { exitProcess(0) }) {
Text("Quitter")
}
}
}

View File

@@ -1,2 +1,25 @@
package com.sio.gestionclients.data.local
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.sio.gestionclients.data.local.dao.ClientDao
import com.sio.gestionclients.data.local.entities.Client
@Database(entities = [Client::class], version=1)
abstract class AppDatabase : RoomDatabase() {
abstract fun clientDao(): ClientDao
companion object {
@Volatile private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase =
INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"client_database"
).build().also { INSTANCE = it }
}
}
}

View File

@@ -1,2 +1,26 @@
package com.sio.gestionclients.data.local.dao
import androidx.room.*
import com.sio.gestionclients.data.local.entities.Client
import kotlinx.coroutines.flow.Flow
@Dao
interface ClientDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(client: Client): Long
@Query("SELECT * FROM clients WHERE synced = 0")
suspend fun getAllClients(): List<Client>
@Query("SELECT * FROM clients")
fun getAllClientsFlow(): Flow<List<Client>>
@Update
suspend fun update(client: Client)
@Query("SELECT * FROM clients WHERE id = :numero")
suspend fun getClientByNumero(numero: Long): Client?
@Delete
suspend fun deleteClient(client: Client)
}

View File

@@ -1,2 +1,14 @@
package com.sio.gestionclients.data.local.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "clients")
data class Client(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val nom: String,
val prenom: String,
val email: String,
val telephone: String,
val synced: Boolean = false
)

View File

@@ -0,0 +1,18 @@
package com.sio.gestionclients.data.repository
import com.sio.gestionclients.data.local.dao.ClientDao
import com.sio.gestionclients.data.local.entities.Client
class LocalClientRepository(private val clientDao: ClientDao) {
suspend fun addClient(client: Client) {
clientDao.insert(client)
}
fun getAllClients() = clientDao.getAllClientsFlow()
suspend fun getClientByNumero(numero: Long): Client? = clientDao.getClientByNumero(numero)
suspend fun updateClient(client: Client) = clientDao.update(client)
suspend fun deleteClient(client: Client) = clientDao.deleteClient(client)
}

View File

@@ -1,2 +1,138 @@
package com.sio.gestionclients.ui.screens
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.sio.gestionclients.data.local.AppDatabase
import com.sio.gestionclients.data.local.entities.Client
import com.sio.gestionclients.data.repository.LocalClientRepository
import com.sio.gestionclients.viewmodel.LocalClientViewModel
import com.sio.gestionclients.viewmodel.LocalClientViewModelFactory
class AjoutClientActivity : ComponentActivity() {
private val viewModel: LocalClientViewModel by viewModels {
LocalClientViewModelFactory(
LocalClientRepository(
AppDatabase.getDatabase(this).clientDao()
)
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AjoutClientScreen(onQuit = { finish() }, onAdd = {
client ->
viewModel.addClient(client)
finish()
})
}
}
}
//@Composable
//fun AjoutClientScreen(onQuit: () -> Unit) {
// Column(Modifier.fillMaxSize().padding(26.dp)) {
// Text("Ajouter un client", fontSize = 26.sp)
// Button(onClick = {}) { Text("Ajouter") }
// Button(onClick = onQuit) { Text("Quitter") }
// }
//}
@Composable
fun AjoutClientScreen(onQuit: () -> Unit, onAdd: (Client) -> Unit) {
//val context = LocalContext.current // <-- IMPORTANT
var nom by remember { mutableStateOf("") }
var prenom by remember { mutableStateOf("") }
var telephone by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
Column(
modifier = Modifier
.padding(20.dp)
.fillMaxSize(),
verticalArrangement = Arrangement.Top
) {
Text(
text = "Ajouter un client",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 20.dp)
)
OutlinedTextField(
value = nom,
onValueChange = { newValue -> nom = newValue },
label = { Text("Nom") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = prenom,
onValueChange = { newValue -> prenom = newValue },
label = { Text("Prénom") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = telephone,
onValueChange = { newValue -> telephone = newValue },
label = { Text("Téléphone") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = email,
onValueChange = { newValue -> email = newValue },
label = { Text("Email") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(20.dp))
Row {
Button(onClick = {onQuit()}){
Text("Quitter")
}
Spacer(modifier = Modifier.width(20.dp))
Button(onClick = {
if (nom.isNotBlank() && prenom.isNotBlank()) {
onAdd(
Client(
nom = nom,
prenom = prenom,
telephone = telephone,
email = email
)
)
}
}) {
Text("Ajouter")
}
}
}
}

View File

@@ -1,31 +1,99 @@
package com.sio.gestionclients.ui.screens
import android.os.Bundle
import android.os.PersistableBundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.ViewModelProvider
import com.sio.gestionclients.data.local.AppDatabase
import com.sio.gestionclients.data.repository.LocalClientRepository
import com.sio.gestionclients.viewmodel.LocalClientViewModel
import com.sio.gestionclients.viewmodel.LocalClientViewModelFactory
class ListeClientsActivity : ComponentActivity() {
private lateinit var viewModel: LocalClientViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { ListeClientsScreen(onQuit = { finish() })}
val database = AppDatabase.getDatabase(this)
val clientDao = database.clientDao()
val repository = LocalClientRepository (clientDao)
val factory = LocalClientViewModelFactory (repository)
viewModel = ViewModelProvider(this, factory).get(LocalClientViewModel::class.java)
setContent {
ListeClientsScreen(onQuit = { finish() }, viewModel = viewModel)
}
}
}
@Composable
fun ListeClientsScreen(onQuit: () -> Unit) {
Column(Modifier.fillMaxSize().padding(26.dp)) {
Text("Liste des clients", fontSize = 26.sp)
Button(onClick = onQuit) { Text("Quitter") }
fun ListeClientsScreen(
viewModel: LocalClientViewModel,
onQuit: () -> Unit
) {
val clients by viewModel.clients.collectAsState(initial = emptyList()) //.collectAsState() indique que clients est un Flow<List<Client>>
Column(modifier = Modifier.fillMaxSize().padding(8.dp)) {
Text(
"Liste des clients",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 8.dp)
)
if (clients.isEmpty()) {
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center
) {
Text("Aucun client enregistré")
}
} else {
LazyColumn(modifier = Modifier.weight(1f)) {
items(clients, key = { it.id }) { client ->
Column(modifier = Modifier.padding(8.dp)) {
Text("${client.nom.orEmpty()} ${client.prenom.orEmpty()}")
Text(client.telephone.orEmpty())
Text(client.email.orEmpty())
Divider()
}
}
}
}
Button(
onClick = onQuit,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
) {
Text("Quitter")
}
}
}
}
//@Composable
//fun ListeClientsScreen(onQuit: () -> Unit, viewModel: LocalClientViewModel) {
// Column(Modifier.fillMaxSize().padding(26.dp)) {
// Text("Liste des clients", fontSize = 26.sp)
// Button(onClick = onQuit) { Text("Quitter") }
// }
//}

View File

@@ -1,2 +1,30 @@
package com.sio.gestionclients.ui.screens
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class ModifClientActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { ModifClientScreen(onQuit = { finish() })}
}
}
@Composable
fun ModifClientScreen(onQuit: () -> Unit) {
Column(Modifier.fillMaxSize().padding(26.dp)) {
Text("Modifier un client", fontSize = 26.sp)
Button(onClick = onQuit) { Text("Quitter") }
}
}

View File

@@ -1,2 +1,115 @@
package com.sio.gestionclients.ui.screens
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelProvider
import com.sio.gestionclients.data.local.AppDatabase
import com.sio.gestionclients.data.repository.LocalClientRepository
import com.sio.gestionclients.viewmodel.LocalClientViewModel
import com.sio.gestionclients.viewmodel.LocalClientViewModelFactory
class SuppClientActivity : ComponentActivity() {
private lateinit var viewModel: LocalClientViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val database = AppDatabase.getDatabase(this)
val clientDao = database.clientDao()
val repository = LocalClientRepository (clientDao)
val factory = LocalClientViewModelFactory (repository)
viewModel = ViewModelProvider(this, factory).get(LocalClientViewModel::class.java)
setContent {
SuppClientScreen({ finish() }, viewModel = viewModel)
}
}
}
@Composable
fun SuppClientScreen(onQuit: () -> Unit, viewModel: LocalClientViewModel) {
val clients by viewModel.clients.collectAsState(initial = emptyList())
val selectedClient by viewModel.selectedClient.collectAsState()
var expanded by remember { mutableStateOf(false) }
var selectedId by remember { mutableStateOf<Long?>(null) }
Column(modifier = Modifier.padding(16.dp)) {
// Bouton pour ouvrir le menu déroulant
TextButton(onClick = { expanded = true }) {
Text(
text = clients.find { it.id == selectedId }?.nom ?: "Choisir un client"
)
}
// Menu déroulant
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
clients.forEach { client ->
DropdownMenuItem(
text = { Text(client.nom) },
onClick = {
selectedId = client.id
expanded = false
viewModel.selectClient(client.id)
}
)
}
}
Spacer(modifier = Modifier.height(16.dp))
// Affichage des infos du client sélectionné
selectedClient?.let { client ->
Text("Nom: ${client.nom}")
Text("Prénom: ${client.prenom}")
Text("Email: ${client.email}")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
viewModel.deleteSelectedClient()
selectedId = null
}) {
Text("Supprimer le client")
}
}
Button(
onClick = onQuit,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
) {
Text("Quitter")
}
}
}
//@Composable
//fun SuppClientScreen(onQuit: () -> Unit) {
// Column(Modifier.fillMaxSize().padding(26.dp)) {
// Text("Supprimer un client", fontSize = 26.sp)
//
// Button(onClick = onQuit) { Text("Quitter") }
// }
//}

View File

@@ -1,2 +1 @@
package com.sio.gestionclients.viewmodel
package com.sio.gestionclients.viewmodel

View File

@@ -0,0 +1,49 @@
package com.sio.gestionclients.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sio.gestionclients.data.local.entities.Client
import com.sio.gestionclients.data.repository.LocalClientRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class LocalClientViewModel(
private val repository: LocalClientRepository
) : ViewModel() {
private val _selectedClient = MutableStateFlow<Client?>(null)
val selectedClient: StateFlow<Client?> = _selectedClient
val clients = repository.getAllClients()
fun addClient(client: Client) {
viewModelScope.launch {
repository.addClient(client)
}
}
fun selectClient(numero: Long) {
viewModelScope.launch {
_selectedClient.value = repository.getClientByNumero(numero)
}
}
fun updateSelectedClient() {
viewModelScope.launch {
_selectedClient.value?.let { client ->
repository.updateClient(client)
_selectedClient.value = null
}
}
}
fun deleteSelectedClient() {
viewModelScope.launch {
_selectedClient.value?.let { client ->
repository.deleteClient(client)
_selectedClient.value = null
}
}
}
}

View File

@@ -0,0 +1,15 @@
package com.sio.gestionclients.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.sio.gestionclients.data.repository.LocalClientRepository
class LocalClientViewModelFactory(private val repository: LocalClientRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(LocalClientViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return LocalClientViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}