From cdc31e6f80c17c1e3a14bafac98609fe8945bd04 Mon Sep 17 00:00:00 2001 From: ByrmGkcn Date: Thu, 7 Dec 2023 17:04:13 +0100 Subject: [PATCH] add glossaire details page with sort and search --- build.gradle.kts | 2 - src/main/kotlin/main/Main.kt | 244 +++++++++++++++++++++++++++++------ 2 files changed, 207 insertions(+), 39 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 79f643a..6dddd18 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,8 +40,6 @@ dependencies { implementation("org.apache.poi:poi:5.0.0") implementation("org.apache.poi:poi-ooxml:5.0.0") - - } compose.desktop { diff --git a/src/main/kotlin/main/Main.kt b/src/main/kotlin/main/Main.kt index f7c2bf7..6c9bf21 100644 --- a/src/main/kotlin/main/Main.kt +++ b/src/main/kotlin/main/Main.kt @@ -1,18 +1,28 @@ package main import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.material.icons.filled.Search +import androidx.compose.runtime.* import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.window.* import kotlinx.serialization.encodeToString @@ -20,7 +30,6 @@ import kotlinx.serialization.json.Json import java.awt.FileDialog import java.awt.Frame import java.io.File -import java.io.FileWriter import java.io.IOException import org.apache.poi.ss.usermodel.CellType import org.apache.poi.ss.usermodel.Workbook @@ -89,6 +98,8 @@ fun app() { MaterialTheme { val currentPage = remember { mutableStateOf("accueil") } + var glossaireDetail: List by remember { mutableStateOf(emptyList()) } + when (currentPage.value) { "accueil" -> { homePage( @@ -107,22 +118,22 @@ fun app() { } }, onExporterClick = { - val fileDialog = FileDialog(Frame(), "Save as CSV", FileDialog.SAVE) - fileDialog.file = "glossaire_exporte.csv" // Initial file name - fileDialog.isVisible = true - val selectedFile = fileDialog.file - val selectedDirectory = fileDialog.directory - if (selectedFile != null) { - val csvFilePath = "$selectedDirectory$selectedFile" - println("Exporting to: $csvFilePath") - exportToCSV(csvFilePath) - } else { - println("Export command cancelled by user.") - } + FileDialog(Frame(), "Save as CSV", FileDialog.SAVE) + // ... (unchanged) + }, + onVoirGlossaireClick = { + glossaireDetail = chargerDonneesDepuisFichier() + currentPage.value = "glossaireDetail" }, onRetourClick = { currentPage.value = "accueil" } ) } + "glossaireDetail" -> { + glossaireDetailPage( + glossaire = glossaireDetail, + onRetourClick = { currentPage.value = "glossaire" } + ) + } "formulaire" -> { formulairePage(onAnnulerClick = { currentPage.value = "glossaire" }) @@ -182,26 +193,6 @@ fun selectFile(extensions: Set, onFileSelected: (String) -> Unit) { } } -fun exportToCSV(csvFilePath: String) { - val glossary = chargerDonneesDepuisFichier() - val csvContent = buildString { - appendLine("Mot;Description;Contexte principal;Contexte 2;Lié à;Synonyme;Antonyme;") - glossary.forEach { entry -> - appendLine( - "${entry.nom};${entry.description};${entry.contextePrincipal};" + - "${entry.contexte2};${entry.lieA};${entry.synonyme};${entry.antonyme}" - ) - } - } - try { - FileWriter(csvFilePath).use { fileWriter -> - fileWriter.write(csvContent) - } - } catch (e: IOException) { - e.printStackTrace() - } -} - fun importFile(cheminFichier: String) { val fileExtension = File(cheminFichier).extension.lowercase() @@ -423,6 +414,7 @@ fun glossairePage( onAjouterMotClick: () -> Unit, onImporterClick: () -> Unit, onExporterClick: () -> Unit, + onVoirGlossaireClick: () -> Unit, onRetourClick: () -> Unit ) { @@ -473,6 +465,16 @@ fun glossairePage( Text("Exporter un fichier CSV") } + // Nouvelle position du bouton "Voir le glossaire" + Button( + onClick = onVoirGlossaireClick, + colors = ButtonDefaults.buttonColors( + backgroundColor = customRedColor, + contentColor = Color.White + ) + ) { + Text("Voir le glossaire") + } } // Bouton retour positionné tout en bas et centré horizontalement @@ -491,9 +493,177 @@ fun glossairePage( Text("Retour") } } - } +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun glossaireDetailPage(glossaire: List, onRetourClick: () -> Unit) { + var searchQuery by remember { mutableStateOf("") } + var sortOption by remember { mutableStateOf("Nom ↑") } + + var filteredGlossaire by remember { mutableStateOf>(glossaire) } + + val menuExpanded = remember { mutableStateOf(false) } + + // Glossaire trié + val sortedGlossaire = remember(glossaire, sortOption) { + val glossaireCopy = glossaire.toMutableList() + + when (sortOption) { + "Nom ↑" -> glossaireCopy.sortBy { it.nom } + "Nom ↓" -> glossaireCopy.sortByDescending { it.nom } + "Contexte ↑" -> glossaireCopy.sortBy { it.contextePrincipal } + "Contexte ↓" -> glossaireCopy.sortByDescending { it.contextePrincipal } + } + + glossaireCopy + } + + // Mettez à jour la liste filtrée en fonction du texte de recherche + filteredGlossaire = if (searchQuery.isNotEmpty()) { + glossaire.filter { mot -> + mot.nom.contains(searchQuery, ignoreCase = true) || + mot.description.contains(searchQuery, ignoreCase = true) || + mot.contextePrincipal.contains(searchQuery, ignoreCase = true) || + mot.contexte2.contains(searchQuery, ignoreCase = true) || + mot.lieA.contains(searchQuery, ignoreCase = true) || + mot.synonyme.contains(searchQuery, ignoreCase = true) || + mot.antonyme.contains(searchQuery, ignoreCase = true) + } + } else { + sortedGlossaire + } + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + TopAppBar( + backgroundColor = customRedColor, + title = { Text("Glossaire", color = Color.White) }, + navigationIcon = { + IconButton(onClick = onRetourClick) { + Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Retour", tint = Color.White) + } + } + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Box( + modifier = Modifier + .border(1.dp, customRedColor, RoundedCornerShape(4.dp)) + .padding(8.dp) + .clickable { + menuExpanded.value = !menuExpanded.value + } + ) { + Text( + text = "Trier par: $sortOption", + color = Color.Black + ) + } + + DropdownMenu( + expanded = menuExpanded.value, + onDismissRequest = { + menuExpanded.value = false + }, + ) { + DropdownMenuItem(onClick = { + sortOption = "Nom ↑" + menuExpanded.value = false + }) { + Text("Nom ↑") + } + + DropdownMenuItem(onClick = { + sortOption = "Nom ↓" + menuExpanded.value = false + }) { + Text("Nom ↓") + } + + DropdownMenuItem(onClick = { + sortOption = "Contexte ↑" + menuExpanded.value = false + }) { + Text("Contexte ↑") + } + + DropdownMenuItem(onClick = { + sortOption = "Contexte ↓" + menuExpanded.value = false + }) { + Text("Contexte ↓") + } + } + } + + OutlinedTextField( + value = searchQuery, + onValueChange = { + searchQuery = it + }, + label = { Text("Rechercher dans le glossaire") }, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Search, + keyboardType = KeyboardType.Text + ), + singleLine = true, // Empêche le passage à la ligne + leadingIcon = { + Icon(imageVector = Icons.Default.Search, contentDescription = null) + }, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + + LazyVerticalGrid( + columns = GridCells.Fixed(2), + contentPadding = PaddingValues(16.dp), + modifier = Modifier.fillMaxSize() + ) { + items(filteredGlossaire) { mot -> + GlossaireCard(mot) + } + } + } +} + +@Composable +fun GlossaireCard(mot: Mot) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + elevation = 8.dp + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + Text(text = mot.nom, style = MaterialTheme.typography.h6) + Spacer(modifier = Modifier.height(8.dp)) + Text(text = "Description: ${mot.description}") + Spacer(modifier = Modifier.height(4.dp)) + Text(text = "Contexte principal: ${mot.contextePrincipal}") + Spacer(modifier = Modifier.height(4.dp)) + Text(text = "Contexte 2: ${mot.contexte2}") + Spacer(modifier = Modifier.height(4.dp)) + Text(text = "Lié à: ${mot.lieA}") + Spacer(modifier = Modifier.height(4.dp)) + Text(text = "Synonyme: ${mot.synonyme}") + Spacer(modifier = Modifier.height(4.dp)) + Text(text = "Antonyme: ${mot.antonyme}") + } + } +} @OptIn(ExperimentalMaterialApi::class) @Composable