SPA

Simple Page Application vamos a realizar una aplicación de navegación o de tipo SPA por eso anteriormente se instalo la depedencia react router vamos a volver a reescribir el archivo App.jsx y escribimos el siguiente código.
 
  
  
 import './App.css';
import { Routes, Route } from "react-router-dom"
import Home from "./Home"
import Hemeroteca from "./Hemeroteca"
import Admin from "./Admin"
import Menu from "./Menu"
import Registrar from "./Registrar"
import Dashboard from "./Dashboard"
import PrivateRoute from "./PrivateRoute"
import Comunicadores from './Comunicadores';
import Noticias from './Noticias';
import UploadEvidencia from './UploadEvidencia';



function App() {





    return ( < >
        <
        Routes >
        <
        Route path = "/"
        element = { < Menu / > } >

        <
        Route index element = { < Home / > }
        />


        <
        Route path = "Hemeroteca"
        element = { < Hemeroteca / > }
        /> <
        Route path = "Admin"
        element = { < Admin / > }
        /> 

        <
        /Route>
        
        <Route
						path='Dashboard'
						element={
							<PrivateRoute>
								<Dashboard/>
							</PrivateRoute>
						}
					>
        </Route>

<Route path="Registrar" element=
        {
            <PrivateRoute>
        <Registrar />
        </PrivateRoute>
        } / >
        
        <Route path="Comunicadores" element=
        {
            <PrivateRoute>
        <Comunicadores />
        </PrivateRoute>
        } / >


<Route path="Noticias" element=
        {
            <PrivateRoute>
        <Noticias />
        </PrivateRoute>
        } / >

<Route path="Upload" element=
        {
            <PrivateRoute>
        <UploadEvidencia />
        </PrivateRoute>
        } / >


        <
        /
        Routes >



        <
        />
    );
}

export default App;
  
    
   

Creamos el archivo Menu.jsx
 
  
  
  import './App.css';


import React from 'react'
import { BreadCrumb } from 'primereact/breadcrumb';
import { Outlet,useNavigate } from "react-router-dom";

import "primereact/resources/themes/lara-light-indigo/theme.css";
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";


function Menu() {


    const navigate = useNavigate()

    const items = [
        { label: 'Home', command: () => { navigate('/') } },
        { label: 'Hemeroteca', command: () => { navigate('/Hemeroteca') } },
        { label: 'Admin', command: () => { navigate('/Admin') } }
    

    ];

    return (
        <>
        <div className = "Admin" >
        <BreadCrumb model = { items }/> 


        </div>
     <Outlet/>
        </>  
    );
}

export default Menu;
  
  
  
  
  
    
   

Instalamos axios para consumir el api rest realizada con spring boot y apache cassandra
 
  
  
  npm install --save axios
     
   

java project
Creamos un archivo Home.jsx y escribimos lo siguiente:
 
 
import './App.css';

import { InputText } from 'primereact/inputtext';
import { useRef, useState ,useEffect} from 'react';
import { Fieldset } from 'primereact/fieldset';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { Badge } from 'primereact/badge';
import axios from 'axios';



function Home() {
    //const [text, setText] = useState('');
    //const toastRef = useRef();
    const [isloading, setIsloading] = useState(true);
    const [noticiasData,setNoticiasData] = useState([]);
    useEffect(() => {
        
        
      
        axios
        .get("http://localhost:8888/api/noticias")
        .then((res) => {
            setNoticiasData(res.data);
            setIsloading(false)
        });
       
     },[]);





    
    return ( <
        div className = "Home" >




       <
        header className = "App-header" >
        <Badge value = "Divulgador de México" > </Badge> <br/ >
        {isloading ? (
            <p>No hay registros</p>
        ) : (
            <section className="cards-wrapper">
                {noticiasData.map((data) => (
                   <Fieldset legend={data.nk.tituloNoticia}  key="index">
                       <Badge value={data.tipoNoticia+"   ("+data.nk.fechaPublicacion+")"}> </Badge>
                       <p>{data.descripcion}</p>
                       <img alt={data.evidencia.ek.idArchivo} src={"https://drive.google.com/thumbnail?id="+data.evidencia.ek.idArchivo} />
                       <br/> <Badge value={data.comunicador}> </Badge>
                   </Fieldset>
                   
                   
                   
                   
                   
                ))}
            </section>
        )}








           </header > 
        </div>
    );
}

export default Home;
  
  
    
   

Para la hemeroteca tenemos que realizar una validación que nos acepte todas las fechas anteriores al día actual tenemos que instalar la librería Moment.js para poder restar un día a la fecha actual y realizar la validación instalamos con el siguiente comando
 
  
  

  npm install moment --save 
  
    
   

Creamos el archivo Hemeroteca.jsx
 
  
  
import './App.css';
import React,{ useState, useEffect }from 'react'
import { Badge } from 'primereact/badge';
import { Calendar } from 'primereact/calendar';
import { addLocale } from 'primereact/api';
import { Fieldset } from 'primereact/fieldset';
import { Image } from 'primereact/image';
import axios from 'axios';
import moment from"moment";

function Hemeroteca() {
    const [isloading, setIsloading] = useState(true);
     const [diaAnterior,setDiaAnterior]=useState(moment(new Date()).subtract(1,"days"));
     const [searchInput, setSearchInput] = useState(null);
     const [noticiasData,setNoticiasData] = useState([]);
     const [diaPosterior,setDiaPosterior]=useState(moment(new Date()));
     useEffect(() => {
        
        const fecha=moment(searchInput).format('yyyy-MM-DD HH:mm:ss');
      
        axios
        .get("http://localhost:8888/api/noticiash/"+fecha)
        .then((res) => {
            setNoticiasData(res.data);
            setIsloading(false)
        });
       
     },[searchInput]);


    addLocale('es', {
        firstDayOfWeek: 1,
        dayNames: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
        dayNamesShort: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
        dayNamesMin: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
        monthNames: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
        monthNamesShort: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
        today: 'Hoy',
        clear: 'Limpiar'
    })




    return (
        

        <><
            header className="App-header">







            <div className="card">

                <Badge value="Bienvenido a la Hemeroteca Digital"> </Badge>
                <br /><br />




                <Calendar id="search" maxDate={diaAnterior.toDate()}  value={searchInput}
                    placeholder='Seleccionar Día' locale="es" onChange={(e) => {
                        setSearchInput(e.target.value);
                    } }   dateFormat="mm-dd-yy"   ></Calendar>


{isloading ? (
                    <p>loading...</p>
                ) : (
                    <section className="cards-wrapper">
                        {noticiasData.map((data) => (
                           <Fieldset legend={data.nk.tituloNoticia}  key="index">
                               <Badge value={data.tipoNoticia+"   ("+data.nk.fechaPublicacion+")"}> </Badge>
                               <p>{data.descripcion}</p>
                               <img alt={data.evidencia.ek.idArchivo} src={"https://drive.google.com/thumbnail?id="+data.evidencia.ek.idArchivo} />
                               <br/> <Badge value={data.comunicador}> </Badge>
                           </Fieldset>
                           
                           
                           
                           
                           
                        ))}
                    </section>
                )}




            </div>




        </header></>




       
    );
}

export default Hemeroteca; 
 
   
   

Instalamos react-final-form para formulario de login
 
  
   
  
  
  npm install --save final-form react-final-form
  
  
    
   

java project
Creamos el archivo Admin.jsx
 
  
  
  import './App.css';
import React,{ useState }  from 'react';
import { Form, Field } from 'react-final-form';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
import { InputText } from 'primereact/inputtext';
import { classNames } from 'primereact/utils';
import { Password } from 'primereact/password';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';

function Admin() {



    const [showMessage, setShowMessage] = useState(false);
    const [showMessage2, setShowMessage2] = useState(false);
    const [formData, setFormData] = useState({});
    const navigate = useNavigate();

    const validate = (data) => {
        let errors = {};

        
        if (!data.email) {
            errors.email = 'Email es requerido.';
        }
        else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(data.email)) {
            errors.email = 'email inválido. E.j. example@email.com';
        }

        if (!data.password) {
            errors.password = 'Password es requerido.';
        }

        

        return errors;
    };

    const onSubmit = (data, form) => {
        setFormData(data);
        
        try{
           axios.get('http://localhost:8888/api/usuario/'+data.email)
           .then((res)=>{

            if(res.data.password === data.password) {
                //setShowMessage(true);
                navigate('/dashboard', {
                    replace: true,
                    state: {
                        logged: true,
                        nombre: res.data.nombre
                    },
                });



            }else{

                setShowMessage2(true);
            }


           } , fail => {
            console.error(fail); // Error!
            setShowMessage2(true);
   });
         }
  
          catch (err) {
            setShowMessage2(true);
         }

         

        form.restart();


        }
       
    
        
        
        
        

    const isFormFieldValid = (meta) => !!(meta.touched && meta.error);
    const getFormErrorMessage = (meta) => {
        return isFormFieldValid(meta) && <small className="p-error">{meta.error}</small>;
    };

    const dialogFooter = <div className="flex justify-content-center"><Button label="OK" className="p-button-text" autoFocus onClick={() => setShowMessage(false) } /></div>;
    const dialogFooter2 = <div className="flex justify-content-center"><Button label="OK" className="p-button-text" autoFocus onClick={() => setShowMessage2(false) } /></div>;


    return ( 


        <
        header className = "App-header" >


        <
        h1 > Bienvenido favor de identificarse </h1>


        <div className="form-demo">
        <Dialog visible={showMessage} onHide={() => setShowMessage(false)} position="top" footer={dialogFooter} showHeader={false} breakpoints={{ '960px': '80vw' }} style={{ width: '30vw' }}>
            <div className="flex align-items-center flex-column pt-6 px-3">
                <i className="pi pi-check-circle" style={{ fontSize: '5rem', color: 'var(--green-500)' }}></i>
                <h5>Bienvenido</h5>
                <p style={{ lineHeight: 1.5, textIndent: '1rem' }}>
                    Has sido registrado correctamnete  Bienvenido <b>{formData.email}</b>.
                </p>
            </div>
        </Dialog>
        <Dialog visible={showMessage2} onHide={() => setShowMessage2(false)} position="top" footer={dialogFooter2} showHeader={false} breakpoints={{ '960px': '80vw' }} style={{ width: '30vw' }}>
            <div className="flex align-items-center flex-column pt-6 px-3">
                <i className="pi pi-check-circle" style={{ fontSize: '5rem', color: 'var(--green-500)' }}></i>
                <h5>Lo sentimos</h5>
                <p style={{ lineHeight: 1.5, textIndent: '1rem' }}>
                    Tu password o usuario es incorrecto.
                </p>
            </div>
        </Dialog>

        <div className="flex justify-content-center">
            <div className="card">
                <h5 className="text-center">Iniciar sesión</h5>
                <Form onSubmit={onSubmit} initialValues={{  email: '', password: '' }} validate={validate} render={({ handleSubmit }) => (
                    <form onSubmit={handleSubmit} className="p-fluid">
                        
                        <Field name="email" render={({ input, meta }) => (
                            <div className="field">
                                <span className="p-float-label p-input-icon-right">
                                    <i className="pi pi-envelope" />
                                    <InputText id="email" {...input} className={classNames({ 'p-invalid': isFormFieldValid(meta) })} />
                                    <label htmlFor="email" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Email*</label>
                                </span>
                                {getFormErrorMessage(meta)}
                            </div>
                        )} />
                        <Field name="password" render={({ input, meta }) => (
                            <div className="field">
                                <span className="p-float-label">
                                    <Password id="password" {...input} toggleMask className={classNames({ 'p-invalid': isFormFieldValid(meta) })} feedback={false} />
                                    <label htmlFor="password" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Password*</label>
                                </span>
                                {getFormErrorMessage(meta)}
                            </div>
                        )} />
                       

                        <Button type="submit" label="Submit" className="mt-2" />
                    </form>
                )} />
            </div>
        </div>
    </div>

    </header>


     
    );
}

export default Admin;
  
    
  
   

Creamos el archivo PrivateRoute.jsx Este para validar el dashboard.
 
  
  
  
  
  import { Navigate, useLocation } from 'react-router-dom';

function PrivateRoute  ({ children })  {
	const { state } = useLocation();

	return state?.logged ? children : <Navigate to='/Admin' />;
};

export default PrivateRoute;
  
  
    
   

Creamos el archivo Menu2.jsx
 
  
  
 import './App.css';


import React from 'react'
import { BreadCrumb } from 'primereact/breadcrumb';
import { Outlet,useNavigate } from "react-router-dom";
import { useLocation } from 'react-router-dom';
import "primereact/resources/themes/lara-light-indigo/theme.css";
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";


function Menu2() {

    const { state } = useLocation();
    const navigate = useNavigate()

    const items2= [
        
        { label: 'Home', command: () => { navigate('/',{replace:true,}) } },
        { label: 'Hemeroteca', command: () => { navigate('/Hemeroteca',{replace:true,}) } },
        { label: 'Registrar Comunicador', command: () => { navigate('/Registrar',{
            replace: true,
            state: {
                nombre:state?.nombre,
                apellidoPaterno:state?.apellidoPaterno,
                logged: true
               }})}},
       { label: 'Ver Comunicadores', command: () => { navigate('/Comunicadores',{
            replace: true,
            state: {
            nombre:state?.nombre,
            apellidoPaterno:state?.apellidoPaterno,
            logged: true
            }})}},
            { label:'Agregar Noticia', command: () => { navigate('/Noticias',{
                replace: true,
                state: {
                nombre:state?.nombre,
                apellidoPaterno:state?.apellidoPaterno,
                logged: true
                }})}},
        { label: 'Salir', command: () => { navigate('/Admin',{replace:true,}) } }
           

    ];

    return (
        <>
        <
        div className = "Admin" >
        <
        BreadCrumb model = { items2 }
        /> 


        <
        /div>
     <Outlet/>
        </>  
    );






}

export default Menu2;
  
  
    
   

Creamos el archivo Dashboard.jsx
 
  
  
import './App.css';
import React from 'react'
import { useLocation } from 'react-router-dom';

import { Outlet} from "react-router-dom";
import Menu2 from "./Menu2"


function Dashboard() {
    const { state } = useLocation();

    




    return (
<>
        
       <Menu2/>

        <h1> Bienvenido { state?.nombre} < /h1>


    
    <Outlet/>
    </>
);
}

export default Dashboard;
  
      
   

Creamos el archivo Registrar.jsx
 
   
 import './App.css';
import React,{ useState } from 'react'
import { Outlet } from "react-router-dom";
import { Form, Field } from 'react-final-form';
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { Password } from 'primereact/password';
import { Dialog } from 'primereact/dialog';
import { Divider } from 'primereact/divider';
import { classNames } from 'primereact/utils';
import axios from 'axios';
import Menu2 from "./Menu2"
function Registrar() {

    

   
    const [showMessage, setShowMessage] = useState(false);
    const [formData, setFormData] = useState({});
    

   

    const validate = (data) => {
        let errors = {};

        if (!data.nombre) {
            errors.name = 'El nombre es requerido.';
        }
        if (!data.apellidoPaterno) {
            errors.apellidoPaterno = 'El apellido paterno es requerido';
        }

        if (!data.email) {
            errors.email = 'El email es requerido.';
        }
        else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(data.email)) {
            errors.email = 'Email inválido. ejemplo ejemplo@email.com';
        }

        if (!data.password) {
            errors.password = 'El password es requerido.';
        }


        return errors;
    };

    const onSubmit = (data, form) => {
        

        axios.post('http://localhost:8888/api/usuario',data)
      .then((response) => {
        setFormData(response.data);
        setShowMessage(true);
    
      });
        form.restart();
    };

    const isFormFieldValid = (meta) => !!(meta.touched && meta.error);
    const getFormErrorMessage = (meta) => {
        return isFormFieldValid(meta) && <small className="p-error">{meta.error}</small>;
    };

    const dialogFooter = <div className="flex justify-content-center"><Button label="OK" className="p-button-text" autoFocus onClick={() => setShowMessage(false) } /></div>;
    const passwordHeader = <h6>Generar password</h6>;
    const passwordFooter = (
        <React.Fragment>
            <Divider />
            <p className="mt-2">Sugerencias</p>
            <ul className="pl-2 ml-2 mt-0" style={{ lineHeight: '1.5' }}>
                <li>Una letra minúscula</li>
                <li>Una letra mayúscula</li>
                <li>dígitos</li>
                <li>Minímo 8 carácteres</li>
            </ul>
        </React.Fragment>
    );





    return ( 
        <>

<
        header className = "App-header" >

         <Menu2/>


       
        <div className="form-demo">
            <Dialog visible={showMessage} onHide={() => setShowMessage(false)} position="top" footer={dialogFooter} showHeader={false} breakpoints={{ '960px': '80vw' }} style={{ width: '30vw' }}>
                <div className="flex align-items-center flex-column pt-6 px-3">
                    <i className="pi pi-check-circle" style={{ fontSize: '5rem', color: 'var(--green-500)' }}></i>
                    <h5>Registrado correctamente</h5>
                    <p style={{ lineHeight: 1.5, textIndent: '1rem' }}>
                        Tu cuenta ha sido registrada <b>{formData.nombre}</b> 
                    </p>
                </div>
            </Dialog>

            <div className="flex justify-content-center">
                <div className="card">
                    <h5 className="text-center">Registrar Comunicador</h5>
                    <Form onSubmit={onSubmit} initialValues={{ nombre: '',apellidoPaterno:'',apellidoMaterno:'', email: '', password: '' }} validate={validate} render={({ handleSubmit }) => (
                        <form onSubmit={handleSubmit} className="p-fluid">
                            <Field name="nombre" render={({ input, meta }) => (
                                <div className="field">
                                    <span className="p-float-label">
                                        <InputText id="nombre" {...input} autoFocus className={classNames({ 'p-invalid': isFormFieldValid(meta) })} />
                                        <label htmlFor="nombre" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Nombre*</label>
                                    </span>
                                    {getFormErrorMessage(meta)}
                                </div>
                            )} />
                            <Field name="apellidoPaterno" render={({ input, meta }) => (
                                <div className="field">
                                    <span className="p-float-label">
                                        <InputText id="apellidoPaterno" {...input} autoFocus className={classNames({ 'p-invalid': isFormFieldValid(meta) })} />
                                        <label htmlFor="apellidoPaterno" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Apellido Paterno*</label>
                                    </span>
                                    {getFormErrorMessage(meta)}
                                </div>
                            )} />
                            <Field name="apellidoMaterno" render={({ input, meta }) => (
                                <div className="field">
                                    <span className="p-float-label">
                                        <InputText id="apellidoMaterno" {...input} autoFocus className={classNames({ 'p-invalid': isFormFieldValid(meta) })} />
                                        <label htmlFor="apellidoMaterno" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Apellido Materno</label>
                                    </span>
                                    
                                </div>
                            )} />
                            <Field name="email" render={({ input, meta }) => (
                                <div className="field">
                                    <span className="p-float-label p-input-icon-right">
                                        <i className="pi pi-envelope" />
                                        <InputText id="email" {...input} className={classNames({ 'p-invalid': isFormFieldValid(meta) })} />
                                        <label htmlFor="email" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Email*</label>
                                    </span>
                                    {getFormErrorMessage(meta)}
                                </div>
                            )} />
                            <Field name="password" render={({ input, meta }) => (
                                <div className="field">
                                    <span className="p-float-label">
                                        <Password id="password" {...input} toggleMask className={classNames({ 'p-invalid': isFormFieldValid(meta) })} header={passwordHeader} footer={passwordFooter} />
                                        <label htmlFor="password" className={classNames({ 'p-error': isFormFieldValid(meta) })}>Password*</label>
                                    </span>
                                    {getFormErrorMessage(meta)}
                                </div>
                            )} />
                            
                            

                            <Button type="submit" label="Registrar" className="mt-2" />
                        </form>
                    )} />
                </div>
            </div>
        </div>
        </header>
        <Outlet/>
    </>
       
    );
}

export default Registrar;