I am trying to do the delete function for a CRUD with React & Firestore, I already manage to eliminate it, the problem is the way I get the ID since it always deletes the record that is lower in the table.
function delete()
const handleDelete = async(id) =>{
/* setData([]); */
console.log(id)
try {
/* await db.collection('empleado').doc(id).delete(); */
} catch (error) {
console.log(error)
}
}
get ID
useEffect(()=>{
db.collection('empleado')
.onSnapshot((querySnapshot) =>{
querySnapshot.forEach(doc =>{
setData(data => [ ...data,[doc.data().dleted,doc.data().tipoDoc,doc.data().doc,doc.data().lugarExp,doc.data().nombre,doc.data().direccion, doc.data().telefono,doc.data().email,doc.data().cargo,doc.data().rh,doc.data().eps,doc.data().tiempoPago,doc.data().sueldo]]);
});
})
db.collection('empleado').onSnapshot((snapshot)=>{
setId(snapshot.docs.map((doc) => ({id: doc.id})))
})
},[]);
onClick function button parameter
{
name: "Delete",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<button onClick={ ()=>handleDelete(id) }>
Delete
</button>
);
}
}
},
this is my console.log()
(4) [{…}, {…}, {…}, {…}]
0: {id: "48DIVsf7NLc5ux5vfH76"}
1: {id: "B3Jnpmmtipmi3IwvxKzs"}
2: {id: "cSPnc9lDpgvWNDXsXhfo"}
3: {id: "rLzJuLFfanXTSArA9fd2"}
length: 4
[[Prototype]]: Array(0)
this happen when press button delete (any row button delete), i want upload a image of my table.
Component
import React,{useEffect} from 'react';
import "../../../css/employee.css";
import Dashboard from '../Dashboard';
import { db } from '../../../firebase';
import MUIDataTable from "mui-datatables";
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
function Employee(){
const [data,setData] = React.useState([]);
const[id,setId]=React.useState('');
const[dleted,setDleted]=React.useState('');
const handleDelete = async(id) =>{
/* setData([]); */
console.log(id)
try {
/* await db.collection('empleado').doc(id).delete(); */
} catch (error) {
console.log(error)
}
}
useEffect(()=>{
db.collection('empleado')
.onSnapshot((querySnapshot) =>{
querySnapshot.forEach(doc =>{
setData(data => [ ...data,[doc.data().tipoDoc,doc.data().doc,doc.data().lugarExp,doc.data().nombre,doc.data().direccion, doc.data().telefono,doc.data().email,doc.data().cargo,doc.data().rh,doc.data().eps,doc.data().tiempoPago,doc.data().sueldo]]);
});
})
db.collection('empleado').onSnapshot((snapshot)=>{
setId(snapshot.docs.map((doc) => ({id: doc.id, data:doc.data()})))
})
},[]);
const columns =[{
name: "Delete",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<button onClick={ ()=>handleDelete(id)}>
Delete
</button>
);
}
}
},
"TIPO DOCUMENTO", "DOCUMENTO", "LUGAR EXPEDICION", "NOMBRE","DIRECCION","TELEFONO","EMAIL","CARGO","RH","EPS","TIEMPO SUELDO","SUELDO"];
const options = {
filterType: 'checkbox',
};
return(
<div id="employ">
<div><Dashboard /></div>
<hr/>
<div style={{marginLeft:'18em'}}>
<MUIDataTable
title={"EMPLEADOS"}
data={data}
columns={columns}
options={options}
/>
</div>
</div>
);
}
export default Employee;
Try this:
<button onClick={ ()=>handleDelete(tableMeta.rowData[0])}> //you have to put the index of your id here
Delete
</button>
SOLUCIONADO
COMPONENT Employee
import React,{useEffect} from 'react';
import "../../../css/employee.css";
import Dashboard from '../Dashboard';
import { db } from '../../../firebase';
import MUIDataTable from "mui-datatables";
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
function Employee(){
const [data,setData] = React.useState([]);
const[id,setId]=React.useState('');
const[dleted,setDleted]=React.useState('');
const [tipoDoc, setTipoDoc] = React.useState('');
const [doc, setDoc] = React.useState('');
const [lugarExp, setLugarExp] = React.useState('');
const [nombre, setNombre] = React.useState('');
const [direccion, setDireccion] = React.useState('');
const [telefono, setTelefono] = React.useState('');
const [email, setEmail] = React.useState('');
const [cargo, setCargo] = React.useState('');
const [rh, setRh] = React.useState('');
const [eps, setEps] = React.useState('');
const [tiempoPago, setTiempoPago] = React.useState('');
const [sueldo, setSueldo] = React.useState('');
const handleSubmit = async (e) => {
e?.preventDefault();
setData([]);
db.collection("empleado").add({
tipoDoc: tipoDoc,
doc: doc,
lugarExp: lugarExp,
nombre: nombre,
direccion: direccion,
telefono: telefono,
email: email,
cargo: cargo,
rh:rh,
eps:eps,
tiempoPago:tiempoPago,
sueldo:sueldo,
}).then(() => {
}).catch((error) => {
alert(error.message)
});
}
const handleDelete = async(id,index) =>{
setData([]);
let parse = '';
let parse2='';
console.log(id);
console.log(index);
parse = parseInt(index);
for (let i = 0; i < id.length; i++) {
if(i === parse ){
const element = id[i];
console.log("element",typeof(element))
parse2 = element;
console.log("B3Jnpmmtipmi3IwvxKzs"==parse2)
await db.collection("empleado").doc(parse2).delete().then(() => {
console.log("Document successfully deleted!");
}).catch((error) => {
console.error("Error removing document: ", error);
});
}
}
}
useEffect(()=>{
db.collection('empleado')
.onSnapshot((querySnapshot) =>{
querySnapshot.forEach(doc =>{
setData(data => [ ...data,[doc.data().tipoDoc,doc.data().doc,doc.data().lugarExp,doc.data().nombre,doc.data().direccion, doc.data().telefono,doc.data().email,doc.data().cargo,doc.data().rh,doc.data().eps,doc.data().tiempoPago,doc.data().sueldo]]);
});
})
db.collection('empleado').onSnapshot((snapshot)=>{
setId(snapshot.docs.map((doc) => (doc.id)))
})
},[]);
const columns =[{
name: "Delete",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<button onClick={ ()=>handleDelete(id,`${tableMeta.rowIndex}`)}>
Delete
</button>
);
}
}
},
, "TIPO DOCUMENTO", "DOCUMENTO", "LUGAR EXPEDICION", "NOMBRE","DIRECCION","TELEFONO","EMAIL","CARGO","RH","EPS","TIEMPO SUELDO","SUELDO"];
const options = {
filterType: 'checkbox',
};
return(
<div id="employ">
<div><Dashboard /></div>
<hr/>
<div style={{marginLeft:'18em'}}>
<MUIDataTable
title={"EMPLEADOS"}
data={data}
columns={columns}
options={options}
/>
</div>
<hr />
<hr /><br /><br /><br/>
<Form id="form-contact">
<Row className="mb-3">
<Form.Group as={Col} controlId="formGridState">
<Form.Label>Tipo documento</Form.Label>
<Form.Select defaultValue="-" value={tipoDoc} onChange={e => { setTipoDoc(e.target.value) }}>
<option>-</option>
<option>Tarjeta Identidad</option>
<option>Cedula Ciudadania</option>
<option>Cedula Extranjeria</option>
</Form.Select>
</Form.Group>
<Form.Group as={Col} controlId="formGridEmail">
<Form.Label>Documento</Form.Label>
<Form.Control type="text" value={doc} onChange={e => { setDoc(e.target.value) }} placeholder="0000000" />
</Form.Group>
<Form.Group as={Col} controlId="formGridPassword">
<Form.Label>Lugar expedicion</Form.Label>
<Form.Control type="text" value={lugarExp} onChange={e => { setLugarExp(e.target.value) }} placeholder="municipio/departamento" />
</Form.Group>
</Row>
<Form.Group className="mb-3" controlId="formGridAddress1">
<Form.Label>Nombre</Form.Label>
<Form.Control type="text" placeholder="name" value={nombre} onChange={e => { setNombre(e.target.value) }} />
</Form.Group>
<Form.Group className="mb-3" controlId="formGridAddress2">
<Form.Label>Direccion</Form.Label>
<Form.Control placeholder="Cra - #" value={direccion} onChange={e => { setDireccion(e.target.value) }} />
</Form.Group>
<Row className="mb-3">
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>Telefono </Form.Label>
<Form.Control value={telefono} onChange={e => { setTelefono(e.target.value) }} />
</Form.Group>
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>Email </Form.Label>
<Form.Control value={email} onChange={e => { setEmail(e.target.value) }} />
</Form.Group>
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>Cargo </Form.Label>
<Form.Control value={cargo} onChange={e => { setCargo(e.target.value) }} />
</Form.Group>
<Form.Group as={Col} controlId="formGridState">
<Form.Label>RH</Form.Label>
<Form.Select defaultValue="-" value={rh} onChange={e => { setRh(e.target.value) }}>
<option>-</option>
<option>A+</option>
<option>AB+</option>
<option>O+</option>
</Form.Select>
</Form.Group>
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>EPS </Form.Label>
<Form.Control value={eps} onChange={e => { setEps(e.target.value) }} />
</Form.Group>
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>Tiempo pago </Form.Label>
<Form.Control value={tiempoPago} onChange={e => { setTiempoPago(e.target.value) }} />
</Form.Group>
<Form.Group as={Col} controlId="formGridCity">
<Form.Label>Sueldo </Form.Label>
<Form.Control value={sueldo} onChange={e => { setSueldo(e.target.value) }} />
</Form.Group>
</Row>
<Form.Group className="mb-3" id="formGridCheckbox" style={{textAlign:'left'}}>
<Form.Check type="checkbox" label="Acepto términos del servicio y tratamiento de datos." />
</Form.Group>
<button type="button" class="btn btn-secondary btn-lg" onClick={()=>handleSubmit()} >Agregar</button>
</Form>
<hr/>
<hr/>
</div>
);
}
export default Employee;
Related
I'm working with Reactjs and GraphQL integration. i got a problem when i'm doing mutation for new user.
Scenario :
Creating user using Modals bootstrap. when successful create new user, it shows alert or information success.
Code :
Here's my ModalCreate component code.
import React, { useState, useEffect } from 'react';
import { Button, Modal, Form } from "react-bootstrap";
const ModalCreate = (props) => {
// state for check input component
const [value, setValue] = useState({
username: props.username || '',
email: props.email || '',
fullname: props.full_name || '',
password: props.password || '',
phone: props.phone || '',
address: props.address || '',
groupid: props.group_id,
});
const onChange = event => {
setValue({
...value,
[event.target.name]: event.target.value
})
}
useEffect(() => {
if (props.show) {
document.body.classList.add("modal-open");
}
return () => {
if (document.body.classList.contains("modal-open")) {
document.body.classList.remove("modal-open");
}
};
}, [props.show]);
return (
<Modal show={props.show}>
<Modal.Header>
<Modal.Title> <span>FORMULIR AKUN PENGGUNA</span> </Modal.Title>
</Modal.Header>
<Modal.Body>
<Form onSubmit={e => {
e.preventDefault();
props.action({
variables: {
...value
}
})
}}>
<Form.Group className="mb-3">
<Form.Label>Role Akun</Form.Label>
<Form.Select aria-label="pilih user role" value={value.groupid} onChange={onChange}>
<option value="superadmin">Super Admin</option>
<option value="admin">Admin</option>
<option value="admin_rj ">Admin RJ</option>
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Username</Form.Label>
<Form.Control name="username" value={value.username} onChange={onChange}/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Nama Lengkap</Form.Label>
<Form.Control name="fullname" value={value.fullname} onChange={onChange}/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Email</Form.Label>
<Form.Control type="email" name="email" value={value.email} onChange={onChange}/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Password</Form.Label>
<Form.Control type="password" name="password" value={value.password} onChange={onChange}/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Phone</Form.Label>
<Form.Control type="text" name="phone" value={value.phone} onChange={onChange}/>
</Form.Group>
<Button variant="secondary" type='submit'>
Simpan
</Button>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.onClose}>
Keluar
</Button>
</Modal.Footer>
</Modal>
);
};
export default ModalCreate;
and action/performing mutation in page call index.js :
import React, { useState } from 'react';
import { useQuery, useMutation } from '#apollo/client';
import { Container, Card, Button, InputGroup, FormControl, Form, Spinner } from 'react-bootstrap';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faSearch } from '#fortawesome/fontawesome-free-solid';
import CardInfo from '../../../component/PengaturanPengguna/CardInfo';
import TableUserInfo from '../../../component/PengaturanPengguna/Table';
import { Loading } from '../../../component/Common';
import ModalCreate from '../../../component/PengaturanPengguna/Modals/ModalCreate';
import { GET_ALL_USERS, GET_USER_BY_ID } from '../../../gql/query';
import { REGISTER_USER } from '../../../gql/mutation';
const SearchInput = () => {
return (
<InputGroup className="mb-3">
<InputGroup.Text>
<FontAwesomeIcon icon={faSearch} />
</InputGroup.Text>
<FormControl
type="text"
placeholder="Search..."
/>
</InputGroup>
)
}
const PengaturanPengguna = (props) => {
// refetch and query data
const { data: usersdata, loading: usersloading, error: userserror } = useQuery(GET_ALL_USERS);
const { refetch, loading } = useQuery(GET_ALL_USERS);
// show modals
const [showModal, setShowModal] = useState(false);
// mutation new register user
const [registerUser, { loading: registerloading, error: registererror }] = useMutation(REGISTER_USER, {
refetchQueries: [{ query: GET_USER_BY_ID }, { query: GET_ALL_USERS }],
onCompleted: data => {
console.log(data)
},
onError: err => {
console.error(err);
}
}) ;
const handleRefreshClick = () => {
refetch();
}
const handleShowModal = () => setShowModal(true);
const handleCloseModal = () => setShowModal(false);
if (usersloading || registerloading) return <Loading/>
if (userserror || registererror) return <p>Error!</p>
return (
<Container>
<CardInfo/>
<Card>
<Card.Title>
<span className='base-md text-regular mt-2 std-txt-primary-200'>Data Pengguna Dashboard</span>
</Card.Title>
<Card.Body>
<div className='d-flex justify-content-between'>
<Form inline>
<SearchInput/>
<Button variant='secondary' onClick={handleRefreshClick} disabled={loading}>{loading ? ( <Spinner
as="span"
animation="border"
size="sm"
role="status"
aria-hidden="true"/> ) : 'Muat Ulang'}</Button>
</Form>
<div>
<Button variant='success' onClick={() => { setShowModal(true) }}>Daftar Akun</Button>
</div>
</div>
<TableUserInfo users={usersdata}/>
</Card.Body>
</Card>
{
showModal ? <ModalCreate show={handleShowModal} onClose={handleCloseModal} action={registerUser} /> : null
}
</Container>
)
}
export default PengaturanPengguna;
and here's my mutation :
const REGISTER_USER = gql`
mutation($input: RegisterInput!) {
register(input: $input) {
username
email
full_name
phone
address
group_id
}
}
`;
Error :
I got this error
Also, Network Status Tabs :
I've been try any solution but it still not working, any help will be appreciated, thank you
If you are getting validation error from apollo for required fields then check the form fields may be name attribute is missing and value is not storing inside your state.
I am having a component which contains a button group in which each button shows the time slot on it.User clicks the button, time slot gets selected.I want to put validation if the user does not select the time and error message gets displayed.I do not have any idea of how to validate it can anyone please help me.Thanks in advance.
Here in the image you can see the select timing option it is having a group of toggle buttons wrapped in button group
const EventUpdateModal = ({setIsOpen, event, date, employees, accessToken}) => {
const resourceId = event.getResources()[0].id
const [employeeBookedSlots, setemployeeBookedSlots] = useState([])
const [radios, setradios] = useState([])
const [radioValue, setradioValue] = useState('')
const [loading, setloading] = useState(false)
console.log("event", event)
//function to create time slots
const createTimeSlots = (FromTime, ToTime) => {
let startTime = moment(FromTime, 'HH:mm');
let endTime = moment(ToTime, 'HH:mm');
if (endTime.isBefore(startTime)) {
endTime.add(1, 'day');
}
const timeSlotsArray = [];
while (startTime <= endTime) {
timeSlotsArray.push(new moment(startTime).format('HH:mm'));
startTime.add(30, 'minutes');
}
return timeSlotsArray;
}
useEffect(() => {
const employee = employees.find((employee) => employee.id == resourceId)
const employeeStart = employee.startTime
const employeeEnd = employee.endTime
const timeSlots = createTimeSlots(employeeStart, employeeEnd)
setradios(timeSlots)
setradioValue(new moment(event.start).format("HH:mm"))
setemployeeBookedSlots(employee.bookedSlots)
}, [])
const selectEmployeeHandler = (e) => {
const selectedId = e.target.value;
const employee = employees.find((emp) => emp.id === selectedId)
const employeeStart = employee.startTime
const employeeEnd = employee.endTime
const timeSlots = createTimeSlots(employeeStart, employeeEnd)
setradios(timeSlots)
setemployeeBookedSlots(employee.bookedSlots)
seteditEvent({...editEvent, stylist: employee.name, resourceId: employee.id})
}
const selectServiceHandler = (e) => {
seteditEvent({...editEvent, service: e.target.value})
}
const handleTiming = (event) => {
seteditEvent({ ...editEvent, timing: event.currentTarget.value })
setradioValue(event.currentTarget.value)
}
const fullName = event.extendedProps.name.split(' ')
const firstName = fullName[0]
const lastName = fullName[fullName.length - 1]
const [editEvent, seteditEvent] = useState({
firstName: firstName,
lastName: lastName,
stylist: event.extendedProps.stylist.id,
service: event.extendedProps.stylist.serviceId,
timing: event.start,
date: new Date(date),
resourceId: resourceId,
title: event.title
})
const handleSubmit = async (e) => {
e.preventDefault()
const data = JSON.stringify({
"first_name": editEvent.firstName,
"last_name": editEvent.lastName,
"employee_id": editEvent.stylist,
"service_id": editEvent.service,
"appointment_time": new moment(editEvent.timing, "HH:mm").format('HH:mm A'),
"appointment_date": new moment(editEvent.date).format('YYYY-MM-DD'),
"comments": ""
})
setloading(true)
await axios({
method: 'post',
url: 'endpoint url',
data: data,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${accessToken}`
}
}).then(function (response){
console.log(response.data)
setloading(false)
setIsOpen(false)
}).catch(function (error){
console.log(error)
setloading(false)
})
}
return (
<div>
<Modal.Header closeButton>
<Modal.Title>{event.title}</Modal.Title>
</Modal.Header>
<Form onSubmit={handleSubmit}>
<Modal.Body>
<Container>
<Row className='mb-3'>
<Form.Group as={Col}>
<Form.Label>
First Name
</Form.Label>
<Form.Control type="name" placeholder="FirstName" name="LastName" value={editEvent.firstName} onChange={(event) => seteditEvent({...editEvent, firstName: event.target.value})}/>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>
Last Name
</Form.Label>
<Form.Control type="name" placeholder="LastName" name="LastName" value={editEvent.lastName} onChange={(event) => seteditEvent({...editEvent, lastName: event.target.value})}/>
</Form.Group>
</Row>
<Row className='mb-3'>
<Form.Group as={Col}>
<Form.Label>
Services
</Form.Label>
<Form.Select name="service" value={editEvent.service} onChange={selectServiceHandler}>
{
services.map((service, idx) =>
<option key={idx} value={service.id}>{service}</option>
)
}
</Form.Select>
</Form.Group>
<Form.Group as={Col}>
<Form.Label >
Stylist
</Form.Label>
<Form.Select aria-label="Default select example" name="stylist" value={editEvent.resourceId} onChange={selectEmployeeHandler}>
{
employees.map((employee) =>
<option key={employee.id} value={`${employee.id}`}>{employee.name}</option>)
}
</Form.Select>
</Form.Group>
</Row>
<Row className='mb-3'>
<Form.Group as={Col} controlId="customerName">
<Form.Label>Date</Form.Label>
<InputGroup style={{flexWrap: "nowrap"}}>
<DatePicker selected={editEvent.date}
onChange={(selectedDate) => seteditEvent({...editEvent, date: selectedDate})}
className="form-control border-radius" />
<InputGroup.Text className="feather icon-calendar "/>
</InputGroup>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Select Timing</Form.Label>
<ButtonGroup className="mb-3" style={{display: 'block' }}>
{radios.map((slot, idx) => (
<ToggleButton as={Col}
key={idx}
id={`radio-${idx}`}
type="radio"
variant="primary"
name="timing"
value={slot}
checked={radioValue === slot}
onChange={handleTiming}
disabled={employeeBookedSlots.includes(slot) ? true : false}
style={{ margin: '4px', borderRadius: '20px', padding: '5px 10px'}}
required
>
{slot}
</ToggleButton>
))}
</ButtonGroup>
</Form.Group>
</Row>
<Row className="mb-3">
</Row>
</Container>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" size='sm' onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button variant="primary" size="sm" type="submit" >
{
loading && <Spinner
as="span"
animation="border"
size="sm"
role="status"
aria-hidden="true"/>
}
{
!loading && <span>Update</span>
}
</Button>
</Modal.Footer>
</Form>
</div>
)
}
const mapStateToProps = (state) => ({
employees: state.employees.employees,
accessToken: state.user.accessToken
})
export default connect(mapStateToProps)(EventUpdateModal)
I've tried to upload an image in Cloudinary and also want to save it into my database. Uploading an image in Cloudinary works fine but I can't save it into my database. Whenever I tried to do this it's only using the default image I've set in my model. Also likes setPic is working but for a moment and again it's changed to the default image. Please anybody help me figure out this problem.
Please comment if any other details if you need. Please help me.
Here is the Function
const postDetails = (pics) => {
setPicMessage(null);
if (pics?.type === 'image/jpeg' || pics?.type === 'image/png') {
const data = new FormData();
data.append('file', pics);
data.append('upload_preset', 'codeblogger_profile_image');
data.append('cloud_name', 'dhuej17x0');
fetch('https://api.cloudinary.com/v1_1/dhuej17x0/image/upload', {
method: 'post',
body: data,
})
.then((res) => res.json())
.then((data) => {
setPic(data.secure_url.toString());
console.log(pic);
})
.catch((err) => {
toast.error(err);
});
} else {
setPicMessage('Please Select an Image');
toast.error(picMessage);
}
};
And here is the full Profile.js File
import React, { useEffect, useState } from 'react';
import { Button, Col, Container, Form, InputGroup, Row } from 'react-bootstrap';
import { toast, ToastContainer } from 'react-toastify';
import { useDispatch, useSelector } from 'react-redux';
import { getUserDetails, updateUserProfile } from '../actions/userActions';
import { USER_UPDATE_PROFILE_RESET } from '../constant/userConstants';
const Profile = ({ history }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [pic, setPic] = useState();
const [password, setPassword] = useState('');
const [picMessage, setPicMessage] = useState();
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordType, setPasswordType] = useState('password');
const [passwordType2, setPasswordType2] = useState('password');
const [showPass, setShowPass] = useState(false);
const [showPass2, setShowPass2] = useState(false);
const dispatch = useDispatch();
const userDetails = useSelector((state) => state.userDetails);
const { user } = userDetails;
// console.log(` this is from line 25 ${user}`);
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const userUpdateProfile = useSelector((state) => state.userUpdateProfile);
const { success } = userUpdateProfile;
useEffect(() => {
if (!userInfo) {
history.push('/login');
} else {
if (!user || !user.name || success) {
dispatch({ type: USER_UPDATE_PROFILE_RESET });
dispatch(getUserDetails('profile'));
} else {
setName(user.name);
setEmail(user.email);
setPic(user.pic);
}
}
if (success) {
toast.success('Profile Updated successfully');
}
showPass ? setPasswordType('text') : setPasswordType('password');
showPass2 ? setPasswordType2('text') : setPasswordType2('password');
}, [showPass, showPass2, dispatch, history, success, user, userInfo]);
const postDetails = (pics) => {
setPicMessage(null);
if (pics?.type === 'image/jpeg' || pics?.type === 'image/png') {
const data = new FormData();
data.append('file', pics);
data.append('upload_preset', 'codeblogger_profile_image');
data.append('cloud_name', 'dhuej17x0');
fetch('https://api.cloudinary.com/v1_1/dhuej17x0/image/upload', {
method: 'post',
body: data,
})
.then((res) => res.json())
.then((data) => {
setPic(data.secure_url.toString());
console.log(pic);
})
.catch((err) => {
toast.error(err);
});
} else {
setPicMessage('Please Select an Image');
toast.error(picMessage);
}
};
const submitHandler = (e) => {
e.preventDefault();
if (password !== confirmPassword) {
toast.error('Passwords do not match');
} else {
dispatch(updateUserProfile({ id: user._id, name, email, password }));
}
};
return (
<div className="profilePage mt-4 py-3">
<ToastContainer />
<Container>
<h2>PROFILE</h2>
<Row className="profileContainer">
<Col md={6}>
<Form onSubmit={submitHandler}>
<Form.Group controlId="name" className="mb-2">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
value={name}
placeholder="Name"
onChange={(e) => setName(e.target.value)}
/>
</Form.Group>
<Form.Group controlId="email" className="mb-2">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group controlId="password" className="mb-2">
<Form.Label>New Password</Form.Label>
<InputGroup>
<Form.Control
type={passwordType}
placeholder="New Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<InputGroup.Text>
<i
onClick={() => setShowPass(!showPass)}
className={showPass ? 'fas fa-eye-slash' : 'fas fa-eye'}
style={{ cursor: 'pointer' }}></i>
</InputGroup.Text>
</InputGroup>
</Form.Group>
<Form.Group controlId="confirmPassword" className="mb-2">
<Form.Label>Confirm Password</Form.Label>
<InputGroup>
<Form.Control
type={passwordType2}
placeholder="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<InputGroup.Text>
<i
onClick={() => setShowPass2(!showPass2)}
className={showPass2 ? 'fas fa-eye-slash' : 'fas fa-eye'}
style={{ cursor: 'pointer' }}></i>
</InputGroup.Text>
</InputGroup>
</Form.Group>
<Form.Group controlId="pic" className="mb-2">
<Form.Label>Change Profile Picture</Form.Label>
<Form.Control
onChange={(e) => postDetails(e.target.files[0])}
type="file"
accept=".jpeg,.png,.jpg"
custom="true"
/>
</Form.Group>
<Button
type="submit"
variant="success"
style={{ letterSpacing: '2px' }}>
UPDATE
</Button>
</Form>
</Col>
<Col
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<img src={pic} alt={user.name} className="profilePic" />
</Col>
</Row>
</Container>
</div>
);
};
export default Profile;
In submitHandler method, you are not passing pic variable in updateUserProfile.
use this
dispatch(updateUserProfile({ id: user._id, name, email, password, pic }));
I have a child component called ProductCard that takes props from the parent called CreateNewSales. The onChange function takes two argument and is defined in the parent. If I call that function in the ProductCard, I keep getting this error for the typeahead though every other input except the quantity input seems to be working just fine. Here are the code:
This is the ProductCard:
import React, { useState, Fragment } from 'react';
import { FormGroup, Label, Input, Col} from 'reactstrap';
import { Typeahead } from 'react-bootstrap-typeahead';
import PropTypes from 'prop-types';
import {PlusSquare, MinusSquare} from 'react-feather'
const plusSquare = <PlusSquare/>
const minusSquare = <MinusSquare/>
const ProductCard = (props) => {
console.log(props)
const ProductOptions = [{id:1,name: "Clothes"}, {id:2,name:"Services"}, {id:3,name:"Shows"}, {id:4,name: "Peace"}] //dummy date //res_data.payload.data.product
// const [singleSelections, setSingleSelections] = useState([]);
// from quantityIncrement.js
const [count, setCount] = useState(1)
const decrement = () => {
if(count === 1){
setCount(count)
} else{
setCount(count-1)
}
};
const increment = () => {
setCount(count+1)
};
return(
<Fragment>
<FormGroup>
<Label>Product Name</Label>
<Typeahead
id="basic-typeahead"
labelKey="name"
onChange={(e) => props.handleChange(e, props.index)}
// onInputChange={props.setProductName}
options={ProductOptions}
name={ProductOptions}
selected={props.value.productName}
style={{backgroundColor:"#d5deee"}}
value={props.value.productName}
/>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="quantity">Quantity</Label>
<div style={{display: "flex"}}>
<Input value={count} id="quantity" name="quantity" onChange={e=> setCount(e.target.value)} style={{backgroundColor: "#d5deee"}} />
<div style={{display:"flex", marginTop:"5px"}}><span><i style={{ width: 15, fontSize: 10, padding: 11, color: '#848b97' }} onClick={increment}>{plusSquare}</i></span><span><i style={{ width: 15, fontSize: 12, color: '#848b97' }} onClick={decrement}>{minusSquare}</i></span></div>
</div>
</Col>
<Col>
<Label for="discount">Discount</Label>
<Input type="text" id="discount" onChange = {(e) => props.handleChange(e, props.index)} value={props.value.discount} name="discount" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="price">Price</Label>
<Input type="text" id="price" onChange={(e) => props.handleChange(e, props.index)} value={props.value.price} name="price" style={{backgroundColor:"#d5deee"}} />
</Col>
<Col>
<Label for="amountPaid">Amount Paid</Label>
<Input type="text" id="amountPaid" onChange={(e) => props.handleChange(e, props.index)} value={props.value.amountPaid} name="amountPaid" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
</Fragment>
)
}
ProductCard.propTypes = {
handleChange: PropTypes.func.isRequired,
value: PropTypes.object.isRequired,
// onClickQuantity: PropTypes.func.isRequired
}
export default ProductCard
This is the CreateNewSale:
import React, { Fragment, useState } from 'react';
import {Form, FormGroup, Label, Input, Card, Col, Row, CardBody, Button, ButtonGroup, Table} from 'reactstrap';
import {toast} from 'react-toastify';
import { Typeahead } from 'react-bootstrap-typeahead';
import { withRouter} from 'react-router';
// import {useHistory} from 'react-router-dom';
import {connect} from 'react-redux';
import {validateCreateNewSaleForm, responseErrorParser} from "../../components/authentication/validator"
// productCard.js
import ProductCard from '../../components/sales/ProductCard'
import {Trash2} from 'react-feather'
const trash2 = <Trash2/>
const CreateNewSale = (props) => {
const [, setIsCreatingNewSale] = useState(false)
// const [singleSelections, setSingleSelections] = useState([]);
// from the productCard.js
const [newProductValues, setNewProductValues] = useState([{
productName: [],
discount: "",
price: "",
amountPaid: "",
quantity: "1",
}]);
// const [newProductName, setNewProductName] = useState([{
// newProductNames: [],
// newCustomerName: []
// }])
// const handleInputChange = (event) => {
// setNewProductName({
// ...newProductName,
// [event.target.name]: event.target.value
// });
// }
const [customerName, setCustomerName] = useState([])
const [date, setDate] = useState('')
const [dueDate, setDueDate] = useState('')
const [vat, setVat] = useState('')
// const [visible, setVisible] = useState(false)
const handleChange = (event, index) => {
console.log( event )
const values = [...newProductValues];
values[index][event.target.name] = event.target.value
console.log('=======================>', values)
setNewProductValues(values);
// setNewProductValues({
// ...newProductValues,
// [event.target.name]: event.target.value
// });
}
const handleAddFields = (e) => {
setNewProductValues([...newProductValues, {discount:"", price: "",
amountPaid: "",
quantity: "1",
productName:[]
}])
}
const handleRemoveFields = (index) => {
const values = [...newProductValues];
values.splice(index, 1);
setNewProductValues(values);
}
const customerOptions = [{id: 1, name: "Olalekan"}, {id: 2, name:"Michael"}, {id: 3,name:"Emeka"}, {id:4,name: "Glory"}] //dummy data //res_data.payload.data.customer
const fields = {
customer_name: { default: '', message: 'Please enter an already created customer name' },
product_name: { default: '', message: 'Please enter an already created customer name' },
quantity: { default: '', message: 'Please select a quantity' },
discount: { default: '', message: 'Please enter the discount given' },
price: { default: '', message: 'Please select the price given' },
amount_paid: { default: '', message: 'Please enter the amount paid by the customer' },
date: { default: '', message: 'Please enter date' },
due_date: { default: '', message: 'Please enter due date given' },
vat: { default: '', message: 'Please enter the vat' },
}
const handleCreateNewSale = async (e) => {
e.preventDefault()
setIsCreatingNewSale(true);
const responsePayload = {
customer_name: newProductValues.newCustomerName, //customerName
product_name: newProductValues.newproductNames,
quantity: newProductValues.quantity,
discount: newProductValues.discount,
price: newProductValues.price,
amount_paid: newProductValues.amountPaid,
date: date,
due_date: dueDate,
vat: vat
}
const errors = validateCreateNewSaleForm(responsePayload, fields)
if (errors.isErrors) {
setIsCreatingNewSale(false)
setTimeout(() => {
errors.errors.forEach(e => toast.error(e.message))
}, 400);
} else {
const response = await props.CreateNewSale(responsePayload)
if (response.status) {
const newSale = response.payload.data.id
localStorage.setItem('__grm__act__biz__', newSale.toString())
// props.history.push(`/business/${newSale}.toString/sales/invoice`, {previousLocation: props.location.pathname})
} else {
setIsCreatingNewSale(false)
const payload = response.payload
const errs = responseErrorParser(payload.data)
setTimeout(() => {
errs.forEach(e => toast.error(e.message))
}, 400);
}
}
}
return(
<Fragment>
<div style={{display:"flex", fontFamily:"'Poppins', sans-serif"}}>
<div className="col-lg-10" style={{margin: "0 auto", maxWidth:"500px", width:"100%"}}>
<Form>
<Card>
<CardBody>
<FormGroup>
<Label>Customer Name</Label>
<Typeahead
id="basic-typeahead"
labelKey="name"
onChange={setCustomerName}
options={customerOptions}
selected={customerName}
value={customerName}
name="customerName"
style={{backgroundColor:"#d5deee"}}
/>
</FormGroup>
</CardBody>
</Card>
{ newProductValues.map((newProductValue, index) => (
<div key={index}>
<Card >
<CardBody>
<Col style={{textAlign: "right"}}>
<i onClick={() => handleRemoveFields()} >{trash2}</i>
</Col>
<ProductCard index={index} handleChange={handleChange} value={newProductValue} />
</CardBody>
</Card>
</div>
))}
<Row>
<Col>
<p onClick={() => handleAddFields()} style={{marginLeft:"20px"}}> <span style={{fontSize:"18px"}}>+</span> Add another product</p>
</Col>
</Row>
<Row>
<Col>
<p onClick={() => handleAddFields()} style={{marginLeft:"20px"}}> <span style={{fontSize:"18px"}}>+</span> Add another product</p>
</Col>
</Row>
<Card>
<CardBody>
<FormGroup>
<div className="form-row">
<Col>
<Label for="date">Date</Label>
<Input className="form-control digits" type="date" defaultValue="2018-01-01" value={date} onChange={e => setDate(e.target.value)} id="date" name="date" style={{backgroundColor:"#d5deee"}} />
</Col>
<Col>
<Label for="dueDate">Due Date</Label>
<Input className="form-control digits" type="date" defaultValue="2018-01-01" value={dueDate} onChange={e => setDueDate(e.target.value)} id="dueDate" name="dueDate" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="vat">VAT %</Label>
<Input type="text" id="vat" value={vat} onChange={e => setVat(e.target.value)} style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<div style={{margin:"0 auto", textAlign:"center"}}>
<p style={{fontSize:"12px"}}>Only click cleared if this sales have been paid in full</p>
<Row>
<Col>
<ButtonGroup>
<Button outline color="primary" type="button">Cleared</Button>
<Button outline color="primary" type="button">Not Cleared</Button>
</ButtonGroup>
</Col>
</Row>
</div>
<Row className="m-t-50">
<Col lg={`6`}>
<Button outline color="primary" size="lg" style={{maxWidth:"200px", width:"100%"}}>SAVE</Button>
</Col>
<Col lg={`6`}>
<Button color="primary" size="lg" onClick={e => handleCreateNewSale(e)} style={{maxWidth:"200px", width:"100%"}}>CREATE</Button>
</Col>
</Row>
</CardBody>
</Card>
</Form>
</div>
<div className="col-lg-2" style={{backgroundColor:"#eaf6fd", position:"fixed", right:0, height:"100%",}}>
<Card className="m-t-50">
<CardBody>
<div>You have added <span>0</span> products</div>
</CardBody>
</Card>
<div className="table-responsive">
<Table borderless>
<tbody>
<tr>
<td className="bd-t-none">Sub Total</td>
<td>000 000 000</td>
</tr>
<tr style={{fontWeight:"bold"}}>
<td className="bd-t-none">Total</td>
<td>000 000 000</td>
</tr>
</tbody>
</Table>
</div>
</div>
</div>
</Fragment>
)
}
const mapStateToProps = (state) => ({
requestingCreateNewSale: state.isRequestingCreateNewSale,
});
const actions = {
CreateNewSale: CreateNewSale
};
export default connect(mapStateToProps, actions)(withRouter(CreateNewSale))
And this is the error I keep getting everytime I want to change the value for the productName typeahead:
TypeError: Cannot read property 'name' of undefined
handleChange
src/pages/sales/CreateNewSales.js:54
51 | const handleChange = (event, index) => {
52 | console.log( event )
53 | const values = [...newProductValues];
> 54 | values[index][event.target.name] = event.target.value
| ^ 55 | console.log('=======================>', values)
56 | setNewProductValues(values);
57 | // setNewProductValues({
View compiled
onChange
src/components/sales/ProductCard.jsx:40
37 | <Typeahead
38 | id="basic-typeahead"
39 | labelKey="name"
> 40 | onChange={(e) => props.handleChange(e, props.index)}
| ^ 41 | // onInputChange={props.setProductName}
42 | options={ProductOptions}
43 | name={ProductOptions}
I am doing Form for add product to firebase, but each click on the submit button, although it already has data or not, it still can add in Firebase. I would like to add validation to this case. How could I do that. I would like all file input cannot be left blank, and text file is always text not the number
const ProductForm = ({
products,
createProductRequest,
fetchProductRequest,
loading,
type }) => {
const [values, setValues] = useState({
image: "",
name: "",
price: 0,
description: "",
categoty: "",
});
const [imageAsFile, setImageAsFile] = useState();
useEffect(() => {
if (Object.keys(products).length) {
setValues(products);
}
}, [products]);
useEffect(() => {
if (type === "CREATE_PRODUCT_SUCCESS") {
fetchProductRequest();
}
}, [fetchProductRequest, type]);
const handleInputChange = (event) => {
// Create new product to update
const newPropdudct = {
...values,
[event.target.name]: event.target.value,
};
// Update new product for value
setValues(newPropdudct);
};
const handleFileChange = (event) => {
const image = event.target.files[0]
setImageAsFile(imageAsFile => (image))
console.log(image);
}
const onSave = () => {
createProductRequest(values, imageAsFile);
};
if (loading) {
return (
<Container>
<Loading />
</Container>
);
}
return (
<Container className="product-form">
<Form>
<Form.Group>
<Form.Group>
<Form.File
id="image"
label="Image choose"
value={values.image.name}
onChange={handleFileChange}
/>
</Form.Group>
</Form.Group>
<Form.Group controlId="name">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
placeholder="Enter product name"
value={values.name}
name="name"
onChange={handleInputChange}
>
</Form.Control>
</Form.Group>
<Form.Group controlId="categories">
<Form.Label>Categories</Form.Label>
<Form.Control
as="select"
name="category"
value={values.category}
onChange={handleInputChange}
>
{CATEGORIES.map((category, index) => (
<option key={index}>{category.name}</option>
))}
</Form.Control>
</Form.Group>
<Form.Group controlId="price">
<Form.Label>Price</Form.Label>
<Form.Control
type="number"
placeholder="Enter product price"
value={values.price}
name="price"
onChange={handleInputChange}
/>
</Form.Group>
<Form.Group controlId="description">
<Form.Label>Description</Form.Label>
<Form.Control
as="textarea"
rows="3"
name="description"
value={values.description}
onChange={handleInputChange}
/>
</Form.Group>
<Button btnText="Submit" size="btn-md" handleClick={onSave} />
</Form>
</Container>
);
};
export default ProductForm;
You may start by adding the required attribute to each form control for basic HMTL5 validation.
For more advanced validation logic, you can have this as your onSave content:
const onSave = (event) => {
let invalid = [];
['name', 'description', 'category'].forEach(key => {
if (values[key].match(/[0-9]+/))
invalid.push(key);
});
if (values.name.trim() === '')
invalid.push('name');
if (invalid.length !== 0) {
event.preventDefault();
// Whatever you want to do when the content is invalid
} else {
createProductRequest(values, imageAsFile);
}
};
With this snippet, you have the invalid fields in the invalid array variable.
You can also customize the validation logic.