React render validated Excel columns to a component - javascript

I am in a very complicated situation. I have a React component which is a drag and drop that takes an excel file. I am required to validate some columns from the Excel file. This part I already managed to solve using the validateFile function inside the component. This function compares an array of strings (the columns to validate) to the actual columns inside the Excel file. The part that I don't know how to do is to render the validated columns to a div or something, instead of the Card component which is the drag and drop box. I will share the code and hopefully someone can help me with this. Thanks in advance.
// #ts-nocheck
// ** React Imports
import { useState, Fragment } from "react";
// ** Reactstrap Imports
import {
Card,
CardHeader,
CardTitle,
CardBody,
Button,
ListGroup,
ListGroupItem,
Toast,
ToastBody,
ToastHeader,
Spinner,
Row,
Col,
} from "reactstrap";
import "../../../styles/lots-modals.scss";
// ** Third Party Imports
import { useDropzone } from "react-dropzone";
import toast from "react-hot-toast";
import { FileText, X, DownloadCloud } from "react-feather";
const XLSX = require("xlsx");
//Extra components
import ExcelValidationTable from "./ExcelValidationTable";
const FileUploaderSingle = () => {
// ** State
const [files, setFiles] = useState([]);
const [open, setOpen] = useState(true);
const [showTable, setShowTable] = useState(false);
const closeModal = () => {
setOpen(false);
};
const { getRootProps, getInputProps } = useDropzone({
multiple: false,
onDrop: (acceptedFiles) => {
const xlsFiles = acceptedFiles.filter(
(file) => file.name.endsWith(".xls") || file.name.endsWith(".xlsx")
);
if (xlsFiles.length === 0) {
toast.error(
() => (
<p className="mb-0">
Solo puede subir archivos de tipo{" "}
<span className="fw-bolder">.xls</span> &{" "}
<span className="fw-bolder">.xlsx</span>{" "}
</p>
),
{
style: {
minWidth: "280px",
},
}
);
return;
}
setFiles([...files, ...xlsFiles.map((file) => Object.assign(file))]);
},
});
const renderFilePreview = (file) => {
if (file.type.startsWith("image")) {
return (
<img
className="rounded"
alt={file.name}
src={URL.createObjectURL(file)}
height="28"
width="28"
/>
);
} else {
return <FileText size="28" />;
}
};
const handleRemoveFile = (file) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
const renderFileSize = (size) => {
if (Math.round(size / 100) / 10 > 1000) {
return `${(Math.round(size / 100) / 10000).toFixed(1)} mb`;
} else {
return `${(Math.round(size / 100) / 10).toFixed(1)} kb`;
}
};
const fileList = files.map((file, index) => (
<ListGroupItem
key={`${file.name}-${index}`}
className="d-flex align-items-center justify-content-between"
>
<div className="file-details d-flex align-items-center">
<div className="file-preview me-1">{renderFilePreview(file)}</div>
<div>
<p className="file-name mb-0">{file.name}</p>
<p className="file-size mb-0">{renderFileSize(file.size)}</p>
</div>
</div>
<Button
color="danger"
outline
size="sm"
className="btn-icon"
onClick={() => handleRemoveFile(file)}
>
<X size={14} />
</Button>
</ListGroupItem>
));
const handleRemoveAllFiles = () => {
setFiles([]);
};
// Function to validate the Excel file for sheets and columns.
const validateFile = async (files) => {
const file = files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (e) {
const buffer = new Uint8Array(e.target.result).buffer;
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetNames = workbook.SheetNames;
if (sheetNames.includes("PLANILLAS") && sheetNames.length === 1) {
const sheet = workbook.Sheets["PLANILLAS"];
const headerRow = XLSX.utils.sheet_to_row_object_array(sheet, {
header: 1,
})[0];
const headerValues = Object.values(headerRow).map((value) =>
value
.toUpperCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
);
// console.log(headerValues);
const requiredColumns = [
"PRIMER NOMBRE",
"SEGUNDO NOMBRE",
"TERCER NOMBRE",
"PRIMER APELLIDO",
"SEGUNDO APELLIDO",
"APELLIDO DE CASADA",
"FECHA DE NACIMI. DD/MM/AAAA",
"NACIONALIDAD",
"PAIS NACIMIENTO",
"DEPARTAMENTO NACIMIENTO",
"MUNICIPIO NACIMIENTO",
"GENERO (MARGINAR ENTRE F/M)",
"ESTADO FAMILIAR",
"RESIDENTE",
"SUJETO IMPUESTO SOBRE LA RENTA-(EXTRANJEROS)",
"DUI A 9 DIGITOS SIN GUIONES",
"FECHA DE EXPEDICION DEL DUI (DD-MM-ANO)",
"FECHA DE VENCIMIENTO DEL DUI (DD-MM-ANO)",
"PAIS EXPEDICION DUI",
"DEPARTAMENTO EXPEDICION DUI",
"CIUDAD DE EXPEDICION DUI",
"NUMERO DE NIT (HOMOLOGACION 9 DIGITOS, SIN HOMOL. 14 DIGITOS",
"FECHA DE EXPEDICION DEL NIT DD/MM/AAAA",
"PASAPORTE",
"CARNE DE RESIDENTE (8 DIGITOS SIN GUIONES)",
"CALIDAD MIGRATORIA",
"DIRECCION PRINCIPAL(DOMICILIO)",
"PAIS DOMICILIO",
"DEPARTAMENTO DOMICILIO",
"MUNICIPIO DOMICILIO",
"NUMERO DE TELEFONO CELULAR (8 DIGITOS SIN GUIONES)",
"NUMERO DE TELEFONO FIJO (8 DIGITOS SIN GUIONES)",
"CORREO ELECTRONICO PERSONAL",
"ORIGEN DE INGRESOS",
"TIEMPO DE LABORAR EN LA EMPRESA ANOS",
"TIEMPO DE LABORAR EN LA EMPRESA MESES",
"PROFESION",
"OCUPACION U OFICIO",
"EMPRESA",
"PUESTO",
"DIRECCION DE LA EMPRESA",
"PAIS EMPRESA",
"DEPTO EMPRESA",
"MUNI EMPRESA",
"TELEFONO FIJO OFICINA - SIN GUIONES",
"TELEFONO CONTACTO - SIN GUIONES",
"SUELDO MENSUAL",
"PAIS ACTIVIDAD ECONOMICA",
"DEPARTAMENTO ACTIVIDAD ECONOMICA",
"MUNICIPIO ACTIVIDAD ECONOMICA",
];
if (headerValues.every((value) => requiredColumns.includes(value))) {
toast.success(
() => (
<p className="mb-0">
<strong>Carga exitosa!</strong>
</p>
),
{
style: {
minWidth: "280px",
margin: "0 auto",
},
}
);
return true;
} else {
toast.error(
() => (
<p className="mb-0">
<strong>Faltan columnas requeridas.</strong>
</p>
),
{
style: {
minWidth: "280px",
margin: "0 auto",
},
}
);
return false;
}
} else if (sheetNames.includes("PLANILLAS")) {
toast.error(
() => (
<p className="mb-0">
Se detectaron otras hojas en el archivo. Solo debe contener la
hoja
<strong> PLANILLAS.</strong>
</p>
),
{
style: {
minWidth: "280px",
margin: "0 auto",
},
}
);
return false;
} else {
toast.error(
() => (
<p className="mb-0">
No se encontró la hoja
<strong> PLANILLAS </strong>
en el archivo.
</p>
),
{
style: {
minWidth: "280px",
margin: "0 auto",
},
}
);
return false;
}
};
};
return (
<>
{open && !showTable && (
<Card>
<CardHeader></CardHeader>
<CardBody>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<div className="d-flex align-items-center justify-content-center flex-column">
<DownloadCloud size={64} />
<h5>Arrastre el archivo aquí o haga click para cargar</h5>
<p className="drag-drop-text-secondary">
Arrastre archivos aquí o haga click en{" "}
<a href="/" onClick={(e) => e.preventDefault()}>
buscar
</a>{" "}
para importar el archivo.
</p>
</div>
</div>
{files.length ? (
<Fragment>
<ListGroup className="my-2">{fileList}</ListGroup>
<div className="d-flex justify-content-end">
<Button
className="me-1"
color="danger"
outline
onClick={handleRemoveAllFiles}
>
Eliminar Archivo
</Button>
<Button
color="primary"
onClick={() => {
validateFile(files);
setShowTable(true);
}}
>
Cargar Archivo
</Button>
</div>
</Fragment>
) : null}
</CardBody>
</Card>
)}
{showTable && <ExcelValidationTable />}
</>
);
};
export default FileUploaderSingle;
I tried asking chatgpt but nothing works. I am just a junior and landed on this project that is too much for me. I am trying to complete this task and I will let them know that this is not for me.

Related

Formik scroll to the first invalid section after clicking on submit button

I am using Formik in my react project, I have implemented all the validations with yup and succesfully showing the errors. One thing i want is if user clicks on submit button i want to scroll it to the section which has validation errors in it.
I tried a way but couldnt achieve it here is the code below
but it gives me error
document.getElementsByName(Object.keys(formik.errors)[0])[0].focus() is undefined
but if the first div passes validation and error is on input box it works fine
const mortgageData = [
"Estou a obter um crédito habitação",
"Quero comparar com o seguro que tenho ",
"O meu seguro renova em breve",
];
const formik = useFormik({
initialValues: {
insuranceMotive: null,
creditCapital: null,
},
onSubmit: (values) => {
console.log(values);
},
validationSchema: validationSchemaSectionOne, //schema which validates the input with the help of Yup
});
useEffect(() => {
// this is the code which i tried but the problem is my first
// field is not input, its a <CheckTable/> component which
// is a div that has text in it, so this code scrolls to input
// box and ignores the first div, even it has errors
if (!formik.isSubmitting) return;
if (Object.keys(formik.errors).length > 0) {
document.getElementsByName(Object.keys(formik.errors)[0])[0].focus();
}
}, [formik]);
return (
<div className="firstSection">
<form onSubmit={formik.handleSubmit}>
<p className="tab-text">
Porque estás á procura de um Seguro Vida Crédito Habitação?
</p>
<div className="selectable-tags-mortgage">
{mortgageData.map((tag, index) => (
<CheckableTag
tag={tag}
key={index}
setValue={(val) => formik.setFieldValue("insuranceMotive", val)}
value={formik.values.insuranceMotive}
/>
))}
</div>
{formik.errors.insuranceMotive && formik.touched.insuranceMotive && (
<div className="formik-errors">{formik.errors.insuranceMotive}</div>
)}
<p className="tab-text">Qual o capital em € a cobrir pelo seguro? </p>
<Input
size="large"
name="creditCapital"
className="input-credit-capital"
value={formik.values.creditCapital}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
{formik.errors.creditCapital && formik.touched.creditCapital && (
<div className="formik-errors">{formik.errors.creditCapital}</div>
)}
<button type="submit" className="button-enabled">
CONTINUAR
</button>
</form>
</div>
);
}
CheckableTag component
export default function CheckableAntTag({ tag, value, setValue }) {
const { CheckableTag } = Tag
return (
<CheckableTag
style={{
backgroundColor: tag === value ? '#AACECD' : 'white'
}}
className="selectable-ant-tags"
key={tag}
checked={tag === value}
onChange={() => setValue(tag)}>
{tag}
</CheckableTag>
)
}
So if insuranceMotive is null, its an error as i have added required to that field, the error message gets logged successfully but i want to scroll to the CheckableTag section instead of input
Instead of using
Object.keys(formik.errors)[0])[0].focus()
you should use
Object.keys(formik.errors)[0])[0].scrollIntoView({ block: 'center'})
Object.keys(formik.errors)[0])[0].focus({ preventScroll: true})

error in the usestate or in the uuid library?

I'm doing a project to manage patient appointments and everything was fine until I installed the 'uuid' library, the library didn't give me a problem, but from then on it gave me this error, apparently it is with the useState but not I find it, I thank you in advance!
error image...
enter image description here
import React, { Fragment, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
const Form = () => {
//creando el State para las citas
const [ cita, actualizarCita ] = useState({
mascota: '',
propietario: '',
fecha: '',
hora: '',
sintomas: ''
});
const [ error, actualizarError ] = useState(false)
// funcion que se ejecutara cada vez que el usuario escribe en el input para validar lo que hace
const actualizarState = e => {
actualizarCita({
...cita,
[e.target.name]: e.target.value
})
}
// Extraer los valores de cita
const { mascota, propietario, fecha, hora, sintomas } = cita;
// Cuando el usuario envia el formulario
const submitCita = e => {
e.preventDefault();
// Validacion
if(mascota.trim() === '' || propietario.trim() === '' || fecha.trim() === '' || hora.trim() === '' || sintomas.trim() === ''){
actualizarError(true);
return;
}
}
// Eliminando el mensaje previo
actualizarError(false);
// Asignando un ID
cita.id = uuidv4();
return (
<Fragment>
<h2> Crear Cita </h2>
{ error ? <p className="alerta-error">Todos los campos son obligatorios</p> : null }
<form
onSubmit={submitCita}
>
<label>Nombre Mascota</label>
<input
type="text"
name="mascota"
className="u-full-width"
placeholder="Nombre Mascota"
onChange={actualizarState}
value={mascota}
/>
<label>Nombre Dueño </label>
<input
type="text"
name="propietario"
className="u-full-width"
placeholder="Nombre dueño de la mascota"
onChange={actualizarState}
value={propietario}
/>
<label>Fecha </label>
<input
type="date"
name="fecha"
className="u-full-width"
onChange={actualizarState}
value={fecha}
/>
<label>Hora </label>
<input
type="time"
name="hora"
className="u-full-width"
placeholder="Nombre dueño de la mascota"
onChange={actualizarState}
value={hora}
/>
<label>Sintomas</label>
<textarea
className="u-full-width"
name="sintomas"
onChange={actualizarState}
value={sintomas}
></textarea>
<button
type="submit"
className="u-full-width button-primary"
>Agregar Cita</button>
</form>
</Fragment>
)
}
export default Form;
you can't update state out side function,
actualizarError(false);
you should remove this, otherwise infinite re-render
It's hard to be sure what the problem might be without a proper sandbox (codesandbox for example).
But the error seems to indicate that you've gotten stuck in an infinite loop, which usually means the component is cause its own state dependencies to change, which causes a redraw (which again causes a change, etc.)
I'd start by moving
// Eliminando el mensaje previo
actualizarError(false);
into
const actualizarState = e => {
actualizarCita({
...cita,
[e.target.name]: e.target.value
})
}

how to switch language from an array with react hooks

i want to change the language of a quote, from French to English or inverse when i click on "lang button", the quote is generate randomly from an array when the page start.
this is my array:
const quoteData = [
{
quoteEn:
"Three things cannot be long hidden: the sun, the moon, and the truth.",
quoteFr:
"Trois choses ne peuvent pas être cachées longtemps : le soleil, la lune et la vérité.",
author: "Buddha"
},
{
quoteEn:
"We make a living by what we get, but we make a life by what we give.",
quoteFr:
"On vit de ce que l’on obtient. On construit sa vie sur ce que l’on donne.",
author: "Winston Churchill"
}
export default quoteData;
and this is what i tried:
import React, { useState } from "react";
import quoteData from "./components/quoteData";
import "./App.css";
import { ReactComponent as Github } from "./icons/github-brands.svg";
function App() {
const getRandomQuotes = () => {
const randNumb = Math.floor(Math.random() * quoteData.length);
return quoteData[randNumb];
};
const [quote, setQuote] = useState(getRandomQuotes());
const [author, setAuthor] = useState(getRandomQuotes().author);
const [lang, setLang] = useState("En");
const [nextBtn, setNextBtn] = useState("Next");
const handleClickLang = () => {
if (lang === "Fr") {
setLang("En");
setNextBtn("Next");
setQuote(quote.quoteFr);
} else {
setLang("Fr");
setNextBtn("Suivant");
setQuote(quote.quoteEn);
}
};
return (
<div id="quote-box">
<div className="c1">
<div className="lang">
<button onClick={handleClickLang} className="changeLang">
{lang}
</button>
</div>
<div className="card flow">
<p id="text">{quote}</p>
<p id="author">{author}</p>
</div>
</div>
<div className="c2">
<Github id="github" />
<button onClick={handleClickRandomQuote} id="new-quote">
{nextBtn}
</button>
</div>
</div>
);
}
export default App;
and i get this error : Error: Objects are not valid as a React child (found: object with keys {quoteEn, quoteFr, author}).
You are storing in your quote state an object (the one you randomly select from your array of quoteData), not the string for the quote. This is not bad, as you also want to show the author of the random quote you randomly selected.
Also, when you do:
const [quote, setQuote] = useState(getRandomQuotes());
const [author, setAuthor] = useState(getRandomQuotes().author);
You have to take into account that the first execution of getRandomQuotes will return a random quote that doesn't have to coincide with the second call to getRandomQuotes to get the author. This could cause that your data (quote and author) are not in sync.
So, it seems that storing the author separately from the quote is not necessary. Taking all this into account, you could do something like:
function App() {
const getRandomQuotes = () => {
const randNumb = Math.floor(Math.random() * quoteData.length);
return quoteData[randNumb];
};
const [quote, setQuote] = useState(getRandomQuotes());
const [lang, setLang] = useState("En");
const [nextBtn, setNextBtn] = useState("Next");
const handleClickLang = () => {
if (lang === "Fr") {
setLang("En");
setNextBtn("Next");
} else {
setLang("Fr");
setNextBtn("Suivant");
}
};
return (
<div id="quote-box">
<div className="c1">
<div className="lang">
<button onClick={handleClickLang} className="changeLang">
{lang}
</button>
</div>
<div className="card flow">
<p id="text">{quote[`quote${lang}`]}</p>
<p id="author">{quote.author}</p>
</div>
</div>
<div className="c2">
<Github id="github" />
<button onClick={handleClickRandomQuote} id="new-quote">
{nextBtn}
</button>
</div>
</div>
);
}

PubSub returning false on publish

I'm using PubSub to globalize some states on my React application. I've a "Home" and a "Escolas" Component, "Escolas" is already using PubSub to share his status with a Component called "Filters". Works fine.
But now, my user starts the application on "Home", there, he enter a name on some input and I want to save this value on a topic of PubSub, but when I try to publish, the return is false.
setEscola(collection, e) {
this.setState({ escola: e });
var isPublished = PubSub.publish('escola-filtro', collection);
console.log(isPublished);
}
This is my entire Component:
import React, { Component } from 'react';
import PubSub from 'pubsub-js';
import lupa from '../../img/lupa.png';
import { Link } from 'react-router-dom';
import MenuHome from '../MenuSuperior/MenuHome';
import { listarEscolas } from '../../services/escolas';
import SelectAutocomplete from '../Inputs/SelectAutocomplete';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
escola : '',
escolas : []
}
this.setEscola = this.setEscola.bind(this);
this.testeEstado = this.testeEstado.bind(this);
}
componentDidMount() {
listarEscolas().then(
lista => {
let escolas = [];
lista.results.forEach(function(escola) {
escolas.push({value : escola.codesc, label : escola.nomesc })
});
this.setState({ escolas : escolas });
}
)
}
componentWillMount() {
PubSub.clearAllSubscriptions();
}
buscarEscolas = (e) => {
if (e.target.value.length >= 3) {
let escolas = [];
listarEscolas(e.target.value).then(
lista => {
lista.results.forEach(function(escola) {
escolas.push({value : escola.codesc, label : escola.nomesc });
});
this.setState({ escolas : escolas });
}
)
}
}
setEscola(collection, e) {
// this.setState({ escola: e });
// var isPublished = PubSub.publishSync('escola-filtro', collection);
// console.log(isPublished);
}
testeEstado(event) {
console.log(event.target.value);
var isPublished = PubSub.publishSync('filtro', event.target.value);
console.log(isPublished);
}
render() {
return(
<div>
<MenuHome />
<div className="w-100 mapa-home">
<div className="container d-flex justify-content-center">
<div className="col-lg-5 text-center position-absolute conteudo">
<h2>Aqui você encontra todas as informações sobre sua escola</h2>
<div className="form-group mt-4">
<input type="text" className="form-control form-control-lg" onChange={this.testeEstado} />
<SelectAutocomplete
value={this.state.escola}
collection={this.state.escolas}
className="form-control form-control-lg rounded-pill shadow m-90 d-inline-block"
placeholder="Encontre sua escola pelo nome ou bairro"
onChange={this.setEscola}
onKeyDown={this.buscarEscolas}
/>
<Link className="btn btn-light d-inline-block ml-1" to="/escolas"><img src={lupa} alt="Buscar" /></Link>
</div>
</div>
</div>
</div>
</div>
);
}
}
Try this:
async function testeEstado(event) {
console.log(event.target.value);
var isPublished = await PubSub.publishSync('filtro', event.target.value);
console.log(isPublished);
}
The Async Await model should work for what you are trying to test here. I am uncertain if it will solve the issues if they are more underlying though.

method .map when running item.url stay with the last url on every iteration of the view while keeping changes from each of it

I'm trying to iterate on a group of elements, so far they iterate on render return, but for some reason when trying to use item.url on but the elements stay with the last item.url path had read. how can make that each iteration use its own item.url instead just the last one from the array of objects.
On line 53 of ./CategoriesListContainer.jsx
class CategoriesListContainer extends React.Component {
state = {
redirect: false,
}
setRedirect = () => {
this.setState({
redirect: true
})
}
render (){
const ItemsApp =[
{
url: '/mail',
title: 'Mailing',
description: 'Esto es una descripcion de la carta a usar para el CSV mailing.',
buttonClass: 'btn-success',
},
{
url: '#',
title: 'Excels',
description: 'Esto es una descripcion de la carta a usar para otros modulos.',
buttonClass: 'btn-success',
},
];
return(
<SectionWrapper sectionTitle='Categories'>
{ ItemsApp.map(Item => {
return (
<CardWrapper
key={ Item.title }
customClass='card'
description={ Item.description }
title={ Item.title }
>
{ this.state.redirect ? <Redirect to={ Item.url }/>: null }
<Button onButtonClick={ this.setRedirect } customClass={ Item.buttonClass } title='Entra aqui' />
</CardWrapper>
);
})}
</SectionWrapper>
);
}
};
export default CategoriesListContainer;
I expect to click on each of the buttons and get to the page defined for its URL variable.
In general you want something like this:
import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
state = {
items: [{ name: 'Colin' }, { name: 'Ricardo' }]
}
handleClick = (name) => {
console.log(name)
}
render() {
const { items } = this.state;
return (
<ul>
{items.map(item => (
<li
key={item.name}
onClick={() => this.handleClick(item.name)}>
{item.name}
</li>
))}
</ul>
)
}
}
render(<App />, document.getElementById('root'));
Live example here.

Categories