diff --git a/.github/workflows/android-finalize-once.yml b/.github/workflows/android-finalize-once.yml new file mode 100644 index 0000000..77b1458 --- /dev/null +++ b/.github/workflows/android-finalize-once.yml @@ -0,0 +1,134 @@ +name: Android finalizer once + +on: + push: + branches: + - 'release/android-0.19.0-final-field-flow' + +permissions: + contents: write + +jobs: + finalize: + if: ${{ !contains(github.event.head_commit.message, 'android-finalizer: apply') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: release/android-0.19.0-final-field-flow + fetch-depth: 0 + + - name: Apply final field UX and Spanish copy + run: | + python - <<'PY' + from pathlib import Path + + def patch(path, replacements): + p = Path(path) + text = p.read_text() + for old, new in replacements: + if old not in text: + raise SystemExit(f'Pattern not found in {path}: {old[:120]!r}') + text = text.replace(old, new) + p.write_text(text) + + patch('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/ModernVisitRoot.kt', [ + (' var commonName by rememberSaveable(visit.id) { mutableStateOf("") }\n', + ' var commonName by rememberSaveable(visit.id) { mutableStateOf("") }\n var description by rememberSaveable(visit.id) { mutableStateOf("") }\n'), + (' commonName = ""\n selectedTypeId = null\n', + ' commonName = ""\n description = ""\n selectedTypeId = null\n'), + (' commonName = commonName,\n attributes = buildModernAttributes(type, attributeValues),\n', + ' commonName = commonName,\n description = description,\n attributes = buildModernAttributes(type, attributeValues),\n'), + (' label = { Text("Nombre o código visible *") },\n supportingText = { Text("Usá lo que figura en placa o identifica el elemento en campo.") },\n', + ' label = { Text("Nombre técnico *") },\n supportingText = { Text("Usá la denominación técnica del elemento. El código DH se genera automáticamente.") },\n'), + (' OutlinedTextField(\n value = commonName,\n onValueChange = { commonName = it },\n label = { Text("Nombre habitual") },\n modifier = Modifier.fillMaxWidth(),\n singleLine = true,\n )\n\n selectedType?.attributes?.forEach { definition ->\n', + ' OutlinedTextField(\n value = commonName,\n onValueChange = { commonName = it },\n label = { Text("Nombre habitual (opcional)") },\n modifier = Modifier.fillMaxWidth(),\n singleLine = true,\n )\n OutlinedTextField(\n value = description,\n onValueChange = { description = it },\n label = { Text("Descripción (opcional)") },\n supportingText = { Text("Dato equivalente al alta desde el Dashboard.") },\n modifier = Modifier.fillMaxWidth(),\n minLines = 2,\n )\n\n selectedType?.attributes?.forEach { definition ->\n'), + ('private fun modernStatusLabel(status: String): String = when (status) {\n "PLANNED" -> "Planificada"\n "IN_PROGRESS" -> "En curso"\n "CLOSED" -> "Cerrada"\n "CANCELLED" -> "Cancelada"\n else -> status\n}\n', + 'private fun modernStatusLabel(status: String): String = visitStatusLabelEs(status)\n'), + ]) + + patch('android-app/app/src/main/java/com/korexlabs/dhinspeccion/MainViewModel.kt', [ + (' accuracyM: Double?,\n ) {\n', + ' accuracyM: Double?,\n description: String? = null,\n ) {\n'), + (' commonName = commonName?.trim()?.takeIf { it.isNotBlank() },\n attributes = attributes,\n', + ' commonName = commonName?.trim()?.takeIf { it.isNotBlank() },\n description = description?.trim()?.takeIf { it.isNotBlank() },\n attributes = attributes,\n'), + (' notice = "Acta bloqueada. Su contenido quedó inmutable; ahora deben resolverse las firmas y manifestaciones."\n', + ' notice = "Acta cerrada y pendiente de firma. Su contenido quedó inmutable; las firmas y manifestaciones pueden completarse a continuación."\n'), + (' error = "Un Acta bloqueada es inmutable y no puede volver a borrador."\n', + ' error = "Un Acta pendiente de firma es inmutable y no puede volver a edición."\n'), + (' notice = "${selectedAct?.code ?: "Acta"} SELLADA e inmutable. Podés crear otra Acta o continuar hacia el cierre de la Inspección."\n', + ' notice = "${selectedAct?.code ?: "Acta"} firmada y cerrada. Podés crear otra Acta o continuar hacia el cierre de la Inspección."\n'), + (' notice = "Inspección cerrada. Todas sus Actas quedaron SELLADAS y disponibles para el circuito de oficina."\n', + ' notice = "Inspección cerrada. Todas sus Actas quedaron firmadas y disponibles para el circuito de oficina."\n'), + ]) + + patch('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/ModernMobileActsScreen.kt', [ + (' "El plazo urgente se computará desde el Acta conforme a la política institucional vigente al bloquearla."\n', + ' "El plazo urgente se computará desde el Acta conforme a la política institucional vigente al cerrar su contenido."\n'), + (' listOf("DNI", "CUIL", "PASSPORT", "OTHER").forEach { kind ->\n FilterChip(selected = documentType == kind, onClick = { documentType = kind }, label = { Text(kind) })\n', + ' listOf("DNI", "CUIL", "PASSPORT", "OTHER").forEach { kind ->\n FilterChip(selected = documentType == kind, onClick = { documentType = kind }, label = { Text(documentTypeLabelEs(kind)) })\n'), + (' OutlinedTextField(email, { email = it }, label = { Text("Email") }, modifier = Modifier.fillMaxWidth(), singleLine = true)\n', + ' OutlinedTextField(email, { email = it }, label = { Text("Correo electrónico") }, modifier = Modifier.fillMaxWidth(), singleLine = true)\n'), + (' Text("Finalizar contenido", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)\n', + ' Text("Cerrar Acta", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)\n'), + (' "Al bloquear el Acta, su contenido y sus Hallazgos quedan inmutables.",\n', + ' "Al cerrar el contenido, el Acta queda inmutable y pasa a Pendiente de firma. Podés firmarla ahora o continuar con otra Acta.",\n'), + (' ) { Text("Finalizar y BLOQUEAR Acta") }\n', + ' ) { Text("Cerrar Acta y dejar pendiente de firma") }\n'), + (' Text("Acta BLOQUEADA", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)\n', + ' Text("Acta pendiente de firma", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)\n'), + (' Text("El contenido ya es inmutable. Restan firmas y manifestaciones para poder sellarla.")\n', + ' Text("El contenido ya está cerrado e inmutable. Completá la firma del inspector y la manifestación de la empresa cuando corresponda.")\n'), + (' closure?.closure?.preparedSha256?.let { Text("Hash · $it", style = MaterialTheme.typography.bodySmall) }\n', + ' closure?.closure?.preparedSha256?.let { Text("Huella de integridad · $it", style = MaterialTheme.typography.bodySmall) }\n'), + (' else -> companyOutcome.status\n', + ' else -> humanBackendLabelEs(companyOutcome.status)\n'), + (' Text("SELLAR Acta definitivamente")\n', + ' Text("Firmar y cerrar Acta")\n'), + (' Text("Acta SELLADA e inmutable", fontWeight = FontWeight.Bold)\n', + ' Text("Acta firmada y cerrada", fontWeight = FontWeight.Bold)\n'), + (' Text("Desde este sellado se genera el PDF del Acta y el INF Word editable para el Inspector.")\n', + ' Text("Desde este cierre se genera el PDF del Acta y el informe INF editable (DOCX) para el Inspector.")\n'), + (' SuccessLine("Todas las Actas están SELLADAS")\n', + ' SuccessLine("Todas las Actas están firmadas y cerradas")\n'), + (' "Falta SELLAR: ${pendingActs.joinToString { it.code }}",\n', + ' "Falta firmar y cerrar: ${pendingActs.joinToString { it.code }}",\n'), + ('private fun modernActStatusLabel(status: String): String = when (status) {\n "DRAFT" -> "Borrador"\n "LOCKED" -> "Bloqueada"\n "SEALED" -> "Sellada"\n "CANCELLED" -> "Cancelada"\n else -> status\n}\n', + 'private fun modernActStatusLabel(status: String): String = actStatusLabelEs(status)\n'), + ]) + + patch('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/FieldFindingScreen.kt', [ + (' Text("Gravedad: ${finding.severity ?: "s/d"} · ${finding.status}", style = MaterialTheme.typography.bodySmall)\n', + ' Text("Gravedad: ${finding.severity ?: "s/d"} · ${findingStatusLabelEs(finding.status)}", style = MaterialTheme.typography.bodySmall)\n'), + ]) + + patch('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/LoginGate.kt', [ + ('import androidx.compose.foundation.layout.Arrangement\n', + 'import androidx.compose.foundation.Image\nimport androidx.compose.foundation.layout.Arrangement\n'), + ('import androidx.compose.foundation.layout.fillMaxWidth\n', + 'import androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\n'), + ('import androidx.compose.ui.platform.LocalContext\n', + 'import androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.res.painterResource\n'), + ('import com.korexlabs.dhinspeccion.MainViewModel\n', + 'import com.korexlabs.dhinspeccion.MainViewModel\nimport com.korexlabs.dhinspeccion.R\n'), + (' Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(14.dp)) {\n Text("DH Inspección", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)\n', + ' Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(14.dp), horizontalAlignment = Alignment.CenterHorizontally) {\n Image(\n painter = painterResource(R.drawable.mendoza_logo),\n contentDescription = "Gobierno de Mendoza",\n modifier = Modifier.fillMaxWidth().height(170.dp),\n contentScale = ContentScale.Fit,\n )\n Text("DH Inspección", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)\n'), + (' label = { Text("Usuario o email") },\n', + ' label = { Text("Usuario o correo electrónico") },\n'), + (' Text("DH Inspección", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)\n Text("Ingresá con tu huella")\n', + ' Image(\n painter = painterResource(R.drawable.mendoza_logo),\n contentDescription = "Gobierno de Mendoza",\n modifier = Modifier.fillMaxWidth().height(140.dp),\n contentScale = ContentScale.Fit,\n )\n Text("DH Inspección", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)\n Text("Ingresá con tu huella")\n'), + ]) + PY + + - name: Commit finalizer changes + run: | + set -Eeuo pipefail + git config user.name 'DH Android Finalizer' + git config user.email 'android-finalizer@dhv2.local' + git add android-app/app/src/main/java/com/korexlabs/dhinspeccion + if git diff --cached --quiet; then + echo 'No changes to commit' + exit 0 + fi + git commit -m 'android-finalizer: apply final field flow and Spanish UX' + git push origin HEAD:release/android-0.19.0-final-field-flow