2. Interfaz de usuario con React
1. Crear el proyecto react con Visual Studio
Para crear el proyecto en Visual Studio se operará tal como se indica en el primer punto de esta entrada.
2. Fichero App.tsx
import React, { useState } from 'react'; import './App.css'; import blue from "@mui/material/colors/blue" import Autocomplete from "@mui/material/Autocomplete" import { countries, CountryType, getCountryByCode, getI18nLabelByCode, getNavigatorLanguage, i18nMapCSV } from './i18n/i18n' import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; import Container from '@mui/material/Container'; import {createTheme, responsiveFontSizes, ThemeProvider} from '@mui/material/styles'; import { Alert, Button, Link, Typography } from '@mui/material'; import Grid from '@mui/material/Grid' export default function App() { const [lang, setLang] = useState<string>(getNavigatorLanguage().toLowerCase()) const [langValue, setLangValue] = React.useState<CountryType>( getCountryByCode(lang) ); const [inputValue, setInputValue] = React.useState<string>(""); let theme = createTheme(); theme = responsiveFontSizes(theme); return ( <Container maxWidth="md" > <img src={require(`./images/logotipo-ajuntament.jpg`)} alt="Ajuntament de donde sea ..." width="95%" /> <br /> <br /> <br /> <ThemeProvider theme={theme}> <Typography variant="h3" sx={{color : blue[500] , fontWeight: '400' , fontStretch: 'extra-condensed'}}> {getI18nLabelByCode('consultar',langValue.code, i18nMapCSV)} </Typography> </ThemeProvider> <br /> <br /> <Grid container spacing={2}> <Grid item xs={8} sm={5} md={4} > <Autocomplete value={langValue} onChange={(event: any, newValue: CountryType | null) => { const aVal = newValue ? newValue : countries[0]; setLangValue(aVal); setLang(aVal.code); }} inputValue={inputValue} onInputChange={(event, newInputValue) => { setInputValue(newInputValue); }} id="country-select-demo" size='small' sx={{ width: 200 }} options={countries} autoHighlight getOptionLabel={(option) => option.label} renderOption={(props, option) => ( <Box component="li" sx={{ "& > img": { mr: 2, flexShrink: 0 } }} {...props} > <img loading="lazy" width="20" src={require(`./images/${option.code.toLowerCase()}.png`)} srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`} alt="" /> {option.label} ({option.code}) </Box> )} renderInput={(params) => ( <TextField {...params} hiddenLabel //label=" " inputProps={{ ...params.inputProps, autoComplete: "new-password", // disable autocomplete and autofill }} /> )} /> </Grid> <Grid item xs={2}> <img src={require(`./images/${langValue.code.toLowerCase()}.png`)} width="40" alt="res" /> </Grid> </Grid> <br/> <br/> <p className='normal'>{getI18nLabelByCode('introduir',langValue.code, i18nMapCSV)}</p> <form action="downloadServletCSV" method="post" target="_blank"> <Grid p={1} container spacing={3} > <TextField name="csv" sx={{ m: 1, width: '40ch' }} /> <Button type="submit" variant="contained" sx={{ m: 2, width: '15ch' } }>{getI18nLabelByCode('descarregar',langValue.code, i18nMapCSV)}</Button> </Grid> </form> <br/> <Alert severity="info" sx={{fontSize: '105%' , fontWeight:350}}>{getI18nLabelByCode('tarja',langValue.code, i18nMapCSV)}</Alert> </Container> ) }
3. Fichero App.css
.normal{ padding : 2px; font-weight: 300; font-size: 110%; }
4. Imagenes
Se guardan en la carpeta src/images y contiene
- Las imágenes de las banderas que representan el idioma
- El logotipo del ayuntamiento en cuestión
5. Internacionalización i18n
Se realiza con el siguiente fichero de typescript src/i18n/i18n.tsexport interface CountryType { id: number; code: string; label: string; } export const countries: readonly CountryType[] = [ { id: 0, code: "ca", label: "Valencià" }, { id: 1, code: "es", label: "Español" }, { id: 2, code: "en", label: "English" }, { id: 3, code: "fr", label: "Francaise" }, { id: 4, code: "de", label: "Deustch" }, { id: 5, code: "ro", label: "Românesc" }, { id: 6, code: "it", label: "Italiano" }, ]; export const i18nMapCSV= new Map<string,string[]>([ ['descarregar', ["Descarregar" , "Descargar" , "Download" , "Décharge" , "Entladung" , "Descarcare" , "Scarico" ] ], ['consultar' , ["Consultar documents per CSV" , "Consultar documentos por CSV" , "Look up documents by VSC" , "Consulter les documents par CVS" , "Konsultieren Sie Dokumente nach VSC" , "Consultați documentele după CVS" , "Consulta i documenti per CVS" ] ], ['introduir' , ["Introduir el CSV del document (Codi Segur de Verificació)" , "Introducir el CSV del documento (Código Seguro de Verificación)", "Type the document's VSC (Verification Safe Code)" , "Entrez CVS du document (Code de Vérification Sécurisé)" , "Geben Sie den Verifizierungscode für das sichere Dokument ein" , "Introduceți CVS-ul documentului sau (Codul de Verificare Sigur)", "Inserisci il CVS del documento (Codice di Verifica Sicura)" ] ], ['tarja' , ["Si accedeixes amb certificat de targa, empra Mozilla Firefox" , "Si accedes con certificado de tarjeta, utiliza Mozilla Firefox" , "Use Mozilla Firefox if smart card certificate is needed" , "Utilisez Mozilla Firefox si un certificat de carte à puce est nécessaire" , "Verwenden Sie Mozilla Firefox, wenn ein Smartcard-Zertifikat benötigt wird" , "Utilizați Mozilla Firefox dacă este necesar un certificat de card inteligent", "Inserisci il CVS del documento (Codice di Verifica Sicura)" ] ], ['error' , ["Document no trobat" , "Documento no encontrado" , "Document not found" , "Document introuvable" , "Dokument nicht gefunden" , "Documentul nu a fost găsit" , "Documento non trovato" ] ], ]) //Get the i18n Labels export function getI18nLabel(id: string, lang : number, i18nMap: Map<string, string[]>) { const myArr= i18nMap.get(id) if (!myArr) return 'NO-TROBAT-i18n' else if (myArr.length<=lang) return 'NO-TROBAT-i18n' else return myArr[lang] } export function getI18nLabelByCode(id: string, langcode : string, i18nMap: Map<string, string[]>) { //alert ('id='+ id + ' langcode='+ langcode + ' map='+JSON.stringify(i18nMap)) const myArr= i18nMap.get(id) if (!myArr) return 'NO-TROBAT-i18n' const lang=getCountryIdByCode(langcode) if (myArr.length<=lang) return 'NO-TROBAT-i18n' return myArr[lang] } //@see https://stackoverflow.com/a/52112155 //const getNavigatorLanguage = () => (navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en'; export function getNavigatorLanguage (): string { return navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language || "es"; } export function getNavigatorIdLanguage (): number { const myLang: string = getNavigatorLanguage().toLowerCase().trim().slice(0,2) const country= countries.find((c)=>c.code === myLang ) return (country ? country.id : 0) } export function getCountryByCode(myCode:string) { const country= countries.find((c)=>c.code === myCode.toLowerCase() ) return country? country : countries[0] } export function getCountryIdByCode(myCode:string) { const country= countries.find((c)=>c.code.trim().slice(0,2) === myCode.toLowerCase() ) return country? country.id : 0 }6. Empaquetar el proyecto para su distribución en Tomcat
Para entender como se despliega un proyecto "single page" (SPA) , y mas en concreto de react, es conveniente que se consulte esta entrada.
Veamos los pasos a realizar
1. En el fichero package.json añadir esta entrada (donde csv es el nombre de la aplicación)
"homepage":"/W01-CSV"
NOTA: Si el fichero generado en el proyecto java es csv.war, entonces hay que cambiar el parámetro "homepage":"csv"
quedando así :
{ "name": "my-react-app", "version": "0.1.0", "private": true, "homepage": "/W01-CSV", "dependencies": { "@emotion/react": "^11.10.4", ..... } }
2. Como no hemos usado React-Router se simplifican las cosas no hay qie tocar los componentes de redireccionamiento
3. Ejecutamos:
npm run build
4. Y se genera la carpeta "build" que és la que hay que pasar al proyecto en java. Para ello se copia la carpeta generada del proyecto react csv/build a la carpeta del proyecto java src/main/webapp.
5. Renombramos src/main/webapp/build a src/main/webapp/csv en la carpeta del proyecto java.
6. Crear este fichero web.xml en la carpeta de java src/main/webapp/WEB-INF/ para que redirija las páginas no encontradas (error 404) al servlet spa.jsp que a su vez redirige al fichero index.html
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns = "https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version = "5.0" metadata-complete = "false" > <error-page> <error-code>404</error-code> <location>/spa.jsp</location> </error-page> </web-app>7. Añadir el fichero spa.jsp a la carpeta del proyecto java src/main/webapp/
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%response.setStatus(200);%> <%@include file="./index.html"%>
Comentarios
Publicar un comentario