Add AutoCompletion, Projects and correction

main
Thomas Breil 2023-12-21 09:08:18 +01:00
parent c5de79c00a
commit d02ffb5e00
7 changed files with 471 additions and 47 deletions

View File

@ -40,7 +40,6 @@ dependencies {
implementation("org.apache.poi:poi:5.0.0") implementation("org.apache.poi:poi:5.0.0")
implementation("org.apache.poi:poi-ooxml:5.0.0") implementation("org.apache.poi:poi-ooxml:5.0.0")
} }
compose.desktop { compose.desktop {

View File

@ -21,6 +21,7 @@ fun compareResults(
codeWords: List<String>, codeWords: List<String>,
onBackClick: () -> Unit onBackClick: () -> Unit
) { ) {
println(glossaryWords)
val commonWords = findCommonWords(glossaryWords, codeWords) val commonWords = findCommonWords(glossaryWords, codeWords)
Column( Column(

View File

@ -1,16 +1,18 @@
package main package main
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -18,10 +20,12 @@ import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
var appState = AppState
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun formPage(glossary: Glossary, onCancelClick: () -> Unit) { fun formPage(glossary: Glossary, onCancelClick: () -> Unit) {
// State to track whether to show the snackbar // State to track whether to show the snackbar
var appState = AppState
val requiredFieldsSnackbarVisibleState = remember { mutableStateOf(false) } val requiredFieldsSnackbarVisibleState = remember { mutableStateOf(false) }
val alreadyExistSnackbarVisibleState = remember { mutableStateOf(false) } val alreadyExistSnackbarVisibleState = remember { mutableStateOf(false) }
@ -83,6 +87,7 @@ fun formPage(glossary: Glossary, onCancelClick: () -> Unit) {
) )
) )
TextField( TextField(
value = description.value, value = description.value,
onValueChange = { description.value = it }, onValueChange = { description.value = it },
@ -94,16 +99,84 @@ fun formPage(glossary: Glossary, onCancelClick: () -> Unit) {
) )
) )
var expanded by remember { mutableStateOf(false) }
val glossaryWords = loadDatasFromFile(glossary.jsonFilePath)
var contextList: List<String> = emptyList()
glossaryWords.forEach { word: Word ->
contextList = contextList + word.mainContext
}
var listToShow by remember(mainContext.value) {
mutableStateOf(contextList.filter { it.startsWith(mainContext.value, ignoreCase = true) })
}
TextField( TextField(
value = mainContext.value, value = mainContext.value,
onValueChange = { mainContext.value = it }, onValueChange = {
mainContext.value = it
expanded = true
listToShow = contextList.filter { word -> word.startsWith(mainContext.value, ignoreCase = true) }
},
label = { Text("Contexte principal *") }, label = { Text("Contexte principal *") },
colors = TextFieldDefaults.textFieldColors( colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = customRedColor, focusedIndicatorColor = customRedColor,
unfocusedIndicatorColor = Color.Gray, unfocusedIndicatorColor = Color.Gray,
focusedLabelColor = customRedColor focusedLabelColor = customRedColor
) )
) )
var dropHeight = (listToShow.count() * 60 + 30)
if (dropHeight > 300){
dropHeight = 300
}
if (expanded && mainContext.value.isNotBlank() && listToShow.isNotEmpty()) {
Box(
modifier = Modifier
.height(dropHeight.dp)
.padding(10.dp)
.border(1.dp, Color.Black)
) {
val scrollState = rememberLazyListState()
LazyColumn(
state = scrollState,
modifier = Modifier.padding(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
listToShow.forEach { word ->
item {
Box(
modifier = Modifier
.width(300.dp)
.clickable {
mainContext.value = word
expanded = false
}
.border(1.dp, Color.Black)
.height(50.dp)
.padding(10.dp)
.clip(MaterialTheme.shapes.medium)
) {
Text(text = word)
}
}
}
}
}
}
TextField( TextField(
value = secondaryContext.value, value = secondaryContext.value,
@ -249,6 +322,7 @@ fun formPage(glossary: Glossary, onCancelClick: () -> Unit) {
} }
} }
fun resetFields( fun resetFields(
name: MutableState<String>, name: MutableState<String>,
description: MutableState<String>, description: MutableState<String>,
@ -287,7 +361,7 @@ fun saveDatasInFile(filePath: String, listeWords: List<Word>) {
val json = Json { prettyPrint = true } val json = Json { prettyPrint = true }
try { try {
val content = json.encodeToString(listeWords) val content = json.encodeToString(listeWords)
File(filePath).writeText(content) File(glossaryPath + (appState.selectedProject?.name ) + "/" + filePath).writeText(content)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
@ -295,11 +369,12 @@ fun saveDatasInFile(filePath: String, listeWords: List<Word>) {
fun loadDatasFromFile(filePath: String): List<Word> { fun loadDatasFromFile(filePath: String): List<Word> {
// If file is empty, return empty list // If file is empty, return empty list
if (!File(filePath).exists() || File(filePath).length() == 0L) {
if (!File(glossaryPath + (appState.selectedProject?.name ) + "/" + filePath).exists() || File(glossaryPath + (appState.selectedProject?.name ) + "/" + filePath).length() == 0L) {
return emptyList() return emptyList()
} }
return try { return try {
val content = File(filePath).readText() val content = File(glossaryPath + (appState.selectedProject?.name ) + "/" + filePath).readText()
Json.decodeFromString<List<Word>>(content) Json.decodeFromString<List<Word>>(content)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()

View File

@ -95,8 +95,32 @@ fun glossaryPage(
} }
} }
) )
} }
if (errorImportation.value) {
AlertDialog(
onDismissRequest = { errorImportation.value = false },
title = { Text("Importation Echouée") },
text = { Text("Le fichier n'a pas pu être importé") },
confirmButton = {
Button(
onClick = {
errorImportation.value = false
frame.dispose()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("OK")
}
}
)
}
Button( Button(
onClick = onExportClick, onClick = onExportClick,
modifier = Modifier modifier = Modifier
@ -159,6 +183,7 @@ fun glossaryPage(
} }
var importedSuccessfully = mutableStateOf(false) var importedSuccessfully = mutableStateOf(false)
var errorImportation = mutableStateOf(false)
var exportedSuccessfully = mutableStateOf(false) var exportedSuccessfully = mutableStateOf(false)
fun selectFile(extensions: Set<String>, onFileSelected: (String) -> Unit) { fun selectFile(extensions: Set<String>, onFileSelected: (String) -> Unit) {
@ -243,6 +268,7 @@ fun importCSVFile(glossary : Glossary, filePath: String) {
println(value) println(value)
} }
val mot = header.zip(values).toMap().toMutableMap() val mot = header.zip(values).toMap().toMutableMap()
try {
val nouveauWord = Word( val nouveauWord = Word(
name = mot["Mot"]!!, name = mot["Mot"]!!,
description = mot["Description"]!!, description = mot["Description"]!!,
@ -263,6 +289,10 @@ fun importCSVFile(glossary : Glossary, filePath: String) {
return return
} }
addWordToGlossary(glossary.jsonFilePath, nouveauWord) addWordToGlossary(glossary.jsonFilePath, nouveauWord)
}catch(e : NullPointerException){
errorImportation.value = true
}
} }
importedSuccessfully.value = true importedSuccessfully.value = true
} }

View File

@ -22,14 +22,16 @@ enum class Language {
@Composable @Composable
fun homePage( fun homePage(
languageManager: LanguageManager, languageManager: LanguageManager,
onGlossaryClick: () -> Unit, onProjectClick: () -> Unit,
onCodeToVerifyClick: () -> Unit onCodeToVerifyClick: () -> Unit
) { ) {
val appState = AppState
val noFileSnackbarVisibleState = remember { mutableStateOf(false) } val noFileSnackbarVisibleState = remember { mutableStateOf(false) }
var isCompareClicked by remember { mutableStateOf(false) } var isCompareClicked by remember { mutableStateOf(false) }
var selectedGlossary by remember { mutableStateOf<Glossary?>(null) } var selectedGlossary by remember { mutableStateOf<Glossary?>(null) }
if (!isCompareClicked && selectedGlossary == null) { if (!isCompareClicked && selectedGlossary == null) {
// Utilisez un Box pour placer le drapeau en haut à droite // Utilisez un Box pour placer le drapeau en haut à droite
Box( Box(
@ -61,7 +63,7 @@ fun homePage(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
Button( Button(
onClick = onGlossaryClick, onClick = onProjectClick,
modifier = Modifier.width(200.dp), modifier = Modifier.width(200.dp),
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor, backgroundColor = customRedColor,
@ -124,24 +126,44 @@ fun homePage(
} }
} }
} }
var selectedProject by remember { mutableStateOf<Project?>(null) }
if (isCompareClicked && selectedGlossary == null) {
GlossaryList(glossaries = loadGlossaries()) { glossary -> if (isCompareClicked && selectedProject == null && selectedGlossary == null) {
ProjectList(
projects = loadProjects(),
onProjectSelected = { project ->
selectedProject = project
appState.selectedProject = selectedProject
println("selectedProject = $selectedProject")
},
onBackClick = { isCompareClicked = false; selectedGlossary = null; selectedProject = null }
)
} else if (isCompareClicked && selectedGlossary == null) {
GlossaryList(
glossaries = loadGlossaries(appState.selectedProject!!),
onGlossarySelected = { glossary ->
selectedGlossary = glossary selectedGlossary = glossary
} println("selectedGlossary = $selectedGlossary")
},
onBackClick = { isCompareClicked = false; selectedGlossary = null; selectedProject = null }
)
} else if (isCompareClicked) { } else if (isCompareClicked) {
selectedGlossary?.let { selectedGlossary?.let {
compareResults( compareResults(
glossaryWords = loadDatasFromFile(it.jsonFilePath), glossaryWords = loadDatasFromFile(it.jsonFilePath),
codeWords = mostUsedWordList.keys.toList(), codeWords = mostUsedWordList.keys.toList(),
onBackClick = { isCompareClicked = false; selectedGlossary = null } onBackClick = { isCompareClicked = false; selectedGlossary = null; selectedProject = null }
) )
} }
} }
} }
@Composable @Composable
fun GlossaryList(glossaries: List<Glossary>, onGlossarySelected: (Glossary) -> Unit) { fun GlossaryList(glossaries: List<Glossary>, onGlossarySelected: (Glossary) -> Unit, onBackClick: () -> Unit) {
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@ -190,5 +212,81 @@ fun GlossaryList(glossaries: List<Glossary>, onGlossarySelected: (Glossary) -> U
) )
Spacer(modifier = Modifier.height(6.dp)) Spacer(modifier = Modifier.height(6.dp))
} }
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onBackClick,
colors = androidx.compose.material.ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Retour")
}
}
}
@Composable
fun ProjectList(projects: List<Project>, onProjectSelected: (Project) -> Unit, onBackClick: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Sélectionnez un Projet", style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.height(500.dp)
.padding(10.dp)
) {
val scrollState = rememberLazyListState()
LazyColumn(
state = scrollState,
modifier = Modifier.padding(10.dp).border(1.dp, Color.Black),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(projects) { project: Project ->
Row(
modifier = Modifier.width(200.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
onProjectSelected(project)
},
modifier = Modifier
.width(150.dp)
.padding(10.dp, 0.dp, 0.dp, 0.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text(project.name)
}
}
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
adapter = rememberScrollbarAdapter(scrollState)
)
Spacer(modifier = Modifier.height(6.dp))
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onBackClick,
colors = androidx.compose.material.ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Retour")
}
} }
} }

View File

@ -27,6 +27,13 @@ import java.util.*
val customRedColor = Color(0xFFB70D1B) val customRedColor = Color(0xFFB70D1B)
val currentPage = mutableStateOf("accueil") val currentPage = mutableStateOf("accueil")
val glossaryPath : String = "src/main/resources/projects/"
// Classe pour stocker l'état global
object AppState {
var selectedProject: Project? = null
}
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
@Preview @Preview
@ -41,21 +48,22 @@ fun app() {
var selectedGlossary by remember { mutableStateOf<Glossary?>(null) } var selectedGlossary by remember { mutableStateOf<Glossary?>(null) }
val appState = AppState
val isEmptySnackbarVisibleState = remember { mutableStateOf(false) } val isEmptySnackbarVisibleState = remember { mutableStateOf(false) }
val containsSpaceSnackbarVisibleState = remember { mutableStateOf(false) } val containsSpaceSnackbarVisibleState = remember { mutableStateOf(false) }
val glossaryAlreadyExistsSnackbarVisibleState = remember { mutableStateOf(false) } val glossaryAlreadyExistsSnackbarVisibleState = remember { mutableStateOf(false) }
//Récupérer tous les glossaires à la racine du projet et les ajouter à la liste glossaries
glossaries = loadGlossaries()
when (currentPage.value) { when (currentPage.value) {
"accueil" -> { "accueil" -> {
homePage( homePage(
languageManager, languageManager,
onGlossaryClick = { currentPage.value = "glossaires" }, onProjectClick = { currentPage.value = "projects" },
onCodeToVerifyClick = { currentPage.value = "choixLangage" }, onCodeToVerifyClick = { currentPage.value = "choixLangage" },
) )
} }
@ -82,6 +90,8 @@ fun app() {
modifier = Modifier.padding(10.dp), modifier = Modifier.padding(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp) verticalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
glossaries = loadGlossaries(appState.selectedProject!!)
println(glossaries)
items(glossaries) { glossary -> items(glossaries) { glossary ->
Row( Row(
modifier = Modifier.width(200.dp).fillMaxWidth(), modifier = Modifier.width(200.dp).fillMaxWidth(),
@ -106,7 +116,7 @@ fun app() {
onClick = { onClick = {
// Handle delete glossary action // Handle delete glossary action
glossaries = glossaries.filterNot { it == glossary } glossaries = glossaries.filterNot { it == glossary }
val file = File(glossary.jsonFilePath) val file = File(glossaryPath + (appState.selectedProject?.name ) + "/" + glossary.jsonFilePath)
file.delete() file.delete()
} }
) { ) {
@ -208,10 +218,10 @@ fun app() {
val newGlossary = Glossary(nouveauGlossaireName, "$nouveauGlossaireName.json") val newGlossary = Glossary(nouveauGlossaireName, "$nouveauGlossaireName.json")
glossaries = glossaries + newGlossary glossaries = glossaries + newGlossary
//create new json file //create new json file
val newFile = File(newGlossary.jsonFilePath) val newFile = File(glossaryPath + (appState.selectedProject?.name ) + "/" + newGlossary.jsonFilePath)
newFile.createNewFile() newFile.createNewFile()
//update glossaries list //update glossaries list
glossaries = loadGlossaries() glossaries = loadGlossaries(appState.selectedProject!!)
currentPage.value = "glossaires" // Revenir à la liste des glossaires currentPage.value = "glossaires" // Revenir à la liste des glossaires
}, },
modifier = Modifier modifier = Modifier
@ -240,6 +250,18 @@ fun app() {
} }
} }
"projects" -> {
projectsPage(
currentPage = currentPage
)
}
"nouveauProjet" ->{
newProject(
currentPage = currentPage
)
}
"glossaireOptions" -> { "glossaireOptions" -> {
glossaryPage( glossaryPage(
languageManager, languageManager,
@ -398,10 +420,10 @@ fun app() {
} }
} }
fun loadGlossaries(): List<Glossary> { fun loadGlossaries(project: Project): List<Glossary> {
val glossaries = mutableListOf<Glossary>() val glossaries = mutableListOf<Glossary>()
//Récupérer tous les fichiers json à la racine du projet //Récupérer tous les fichiers json à la racine du projet
val glossaryFiles = File(".").listFiles { file -> val glossaryFiles = File(glossaryPath + project.name).listFiles { file ->
file.extension == "json" file.extension == "json"
} }
glossaryFiles?.forEach { file -> glossaryFiles?.forEach { file ->
@ -413,6 +435,7 @@ fun loadGlossaries(): List<Glossary> {
} }
fun main() = application { fun main() = application {
val state = rememberWindowState( val state = rememberWindowState(
placement = WindowPlacement.Floating, placement = WindowPlacement.Floating,

View File

@ -0,0 +1,198 @@
package main
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import java.io.File
data class Project(val name: String)
fun loadProjects(): List<Project> {
val projectsDirectory = File("src/main/resources/projects/")
return projectsDirectory.listFiles()?.map { Project(it.name) } ?: emptyList()
}
@Composable
fun projectsPage(
currentPage: MutableState<String>
) {
val appState = AppState
var selectedProject: Project? by remember { mutableStateOf(null) }
var projects: List<Project> by remember { mutableStateOf(loadProjects()) }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Sélectionnez un projet", style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(16.dp))
LazyColumn(
modifier = Modifier.padding(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(projects, key = { project -> project.name }) { project ->
Row(
modifier = Modifier.width(200.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
appState.selectedProject = project
currentPage.value = "glossaires"
},
modifier = Modifier
.width(150.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text(project.name)
}
IconButton(
onClick = {
// Handle delete project action
projects = projects.filterNot { it.name == project.name }
println(projects)
val directory = File("src/main/resources/projects/${project.name}/")
directory.deleteRecursively()
currentPage.value = "projects"
}
) {
Icon(imageVector = Icons.Default.Delete, contentDescription = "Delete Project")
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
currentPage.value = "nouveauProjet"
},
modifier = Modifier
.width(300.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Créer un nouveau project")
}
Spacer(modifier = Modifier.height(6.dp))
Button(
onClick = {
currentPage.value = "accueil"
},
modifier = Modifier
.width(250.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Retour")
}
}
}
@Composable
fun mySuperCoolButton(
project: Project,
){
var projects: List<Project> = loadProjects()
IconButton(
onClick = {
// Handle delete glossary action
projects = projects.filterNot { it == project }
val directory = File("src/main/resources/projects/${project.name}/")
directory.deleteRecursively()
}
) {
Icon(imageVector = Icons.Default.Delete, contentDescription = "Delete Project")
}
}
@Composable
fun newProject(
currentPage: MutableState<String>
){
var projectName by remember { mutableStateOf("") }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
){
Text("Nom du projet", style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = projectName,
onValueChange = { projectName = it },
label = { Text("Nom du projet") },
modifier = Modifier
.width(300.dp)
.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
val directory = File("src/main/resources/projects/$projectName/")
directory.mkdirs()
println("Project $projectName created")
currentPage.value = "projects"
},
modifier = Modifier
.width(300.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Créer")
}
Spacer(modifier = Modifier.height(6.dp))
Button(
onClick = {
currentPage.value = "projects"
},
modifier = Modifier
.width(250.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = customRedColor,
contentColor = Color.White
)
) {
Text("Retour")
}
}
}