I'm new to react so pardon any mistake.
I want to update user profile picture but i dont know how can i make a clickable image(variable name ->"avatar" in code) which previews and then update(in database also), if we click save.
Can someone help me adding a clickable image that previews and updates the from and database as well.
If we cannot make clickable image, a separate button for upload and save will also be fine.
Let me know if you want more info about code.
the code i have:
import React from 'react';
import ProfileBanner from '../ProfileBanner';
import coverSrc from 'assets/img/generic/4.jpg';
//import avatar from 'assets/img/team/2.jpg';
import { Col, Row } from 'react-bootstrap';
import ProfileSettings from './ProfileSettings';
//import ExperiencesSettings from './ExperiencesSettings';
//import EducationSettings from './EducationSettings';
//import AccountSettings from './AccountSettings';
import BillingSettings from './BillingSettings';
import ChangePassword from './ChangePassword';
import DangerZone from './DangerZone';
import axios from 'axios';
import Cookies from 'universal-cookie';
import { useEffect ,useState,useRef} from 'react';
import { toast } from 'react-toastify';
const Settings = () => {
const [formData, setFormData] = useState({
avatar: '',
});
//const { avatar} = formData;
const cookies = new Cookies();
const user_id = cookies.get('xyz');
useEffect(async () => {
//e.preventDefault();
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const { data } = await axios.post(
`/api/auth/ImageRetrieve`,
{
user_id
},
config
);
setFormData({
avatar:data.link
});
}
catch (error) {
toast.success('Something went wrong', {
theme: 'colored'
});
}
}, []);
return (
<>
<ProfileBanner>
<div>
<ProfileBanner.Header
coverSrc={coverSrc}
avatar={formData.avatar}
className="mb-8"
/>
</div>
</ProfileBanner>
<Row className="g-3">
<Col lg={8}>
<ProfileSettings />
{/*<ExperiencesSettings />
<EducationSettings />}*/}
<br></br>
<ChangePassword />
</Col>
<Col lg={4}>
<div className="sticky-sidebar">
{/* <AccountSettings />*/}
<BillingSettings />
<DangerZone />
</div>
</Col>
</Row>
</>
);
};
export default Settings;
you can basically add onClick property to any native element, so
<span onClick={()=>{ /*put here some code that change the coverSrc state*/}}>
<ProfileBanner.Header
coverSrc={coverSrc}
avatar={formData.avatar}
className="mb-8"
/>
</span>
you can create a popup to enter new pic URL and than on-close update that state.
According to my understanding of the problem, I think you are looking something like this -
const [selectedAvatar, setSelectedAvatar] = useState();
const [formData, setFormData] = useState({
avatar: '',
});
useEffect(() => {
if (!selectedAvatar) {
setFormData({...formData, avatar: ''});
return;
}
const objectUrl = URL.createObjectURL(selectedAvatar);
//DB call to save objectUrl into the DB
setFormData({...formData, avatar: objectUrl});
return () => URL.revokeObjectURL(objectUrl);
}, [selectedAvatar]);
const onSelectFile = e => {
if (!e.target.files || !e.target.files.length) {
setSelectedAvatar('');
return;
}
setSelectedAvatar(e.target.files[0]);
}
return (
<div>
<input type='file' onChange={onSelectFile} />
{!!selectedAvatar && <div>
<ProfileBanner.Header
coverSrc={coverSrc}
avatar={formData.avatar}
className="mb-8"
/>
</div>
}
</div>
)
Related
I'm trying to build a website where user can put their input with html form and generate the value of user that has enter and display the pdf file with the values of the user input. I'm using NextJs. For pdf reader library I'm using PDF-Lib [here the documentation of pdf-lib] https://pdf-lib.js.org/ and I using React useContext to manage state of user input and store it in the cookie with js-cookies. There no issue with user input and storing value.
Example form picture https://i.stack.imgur.com/phbtT.png
And here the code of the picture above. There no issue in here yet
import React, { useContext, useEffect } from 'react';
import Layout from '../components/Layout';
import { useForm } from 'react-hook-form';
import Cookies from 'js-cookie';
import { Store } from '../utils/Store';
import { useRouter } from 'next/router';
export default function BorangScreen() {
const {
handleSubmit,
register,
formState: { errors },
setValue,
} = useForm();
const { state, dispatch } = useContext(Store);
const { pks } = state;
const { borangPks } = pks;
const router = useRouter();
useEffect(() => {
setValue('totalgrant', borangPks.totalgrant);
}, [setValue, borangPks]);
const submitHandler = ({ totalgrant }) => {
dispatch({
type: 'SAVE_FORM_PKS',
payload: { totalgrant },
});
Cookies.set(
'pks',
JSON.stringify({
...pks,
borangPks: {
totalgrant,
},
})
);
router.push('/pdf');
};
return (
<Layout title="Borang Pks">
<div>
<form
className="mx-auto max-w-screen-md"
onSubmit={handleSubmit(submitHandler)}
>
<div className="mb-4">
<label htmlFor="totalgrant">
Total Grant Amount / Jumlah Geran keseluruhan
</label>
<input
type="number"
maxlength="10"
className="w-full"
id="totalgrant"
autoFocus
{...register('totalgrant', {
required: 'Please enter amount',
maxLength: 10,
})}
/>
{errors.totalgrant && (
<div className="text-red-500">{errors.totalgrant.message}</div>
)}
{errors.totalgrant && errors.totalgrant.type === 'maxLength' && (
<div className="text-red-500">Limit to 10 digit </div>
)}
</div>
<div className="mb-4 flex justify-between">
<button className="primary-button">Next</button>
</div>
</form>
</div>
</Layout>
);
}
BorangScreen.auth = true;
Then when user click next button and will redirect user to the /pdf pages where it will show the pdf file that have generate with user input values. Here the picture of it https://i.stack.imgur.com/8hJBF.png
And here the code inside the pages
import React, { useContext } from 'react';
import { PDFDocument } from 'pdf-lib';
import Layout from '../components/Layout';
import { Store } from '../utils/Store';
const { writeFileSync, readFileSync } = require('fs');
export default function Pdf() {
const { state } = useContext(Store);
const { pks } = state;
const createPdf = async(input, output) => {
try {
// async load pdf document
const pdfDoc = await PDFDocument.load(await readFileSync(input));
// Modify document, fill out the form...
const fieldNames = pdfDoc
.getForm()
.getFields()
.map((f) => f.getName());
console.log({ fieldNames });
const form = pdfDoc.getForm();
// fill form text document
form.getTextField('totalgrant').setText(pks.borangPks.totalgrant);
form.flatten();
const pdfBytes = await pdfDoc.save();
await writeFileSync(output, pdfBytes);
console.log('PDF created!');
} catch (err) {
console.log(err);
}
};
// exec pdf output
createPdf('./public/1.pdf', './public/Borang Geran PKS.pdf'); // (input, output)
return (
<Layout title="Borang PKS">
<div>
<iframe
id="pdf"
type="application/pdf"
style={{ width: '100%', height: '100vh' }}
src="Borang Geran PKS.pdf"
></iframe>
</div>
</Layout>
);
}
So the question/issues is when I'm define the values in here code below and console.log(borangPks.totalgrant) the values is undefined in the terminal but in the chrome devtools it showed the value of the totalgrant: '10000' <-- let say the user enter 10000 for totalgrant form.
export default function Pdf() {
const { state } = useContext(Store);
const { pks } = state;
const { borangPks} = pks;
console.log(borangPks.totalgrant)
And when I'm define the values in here ofcos it will show undefined
form.getTextField('totalgrant').setText(borangPks.totalgrant);
I have try String() function and still undefined
form.getTextField('totalgrant').setText(String(borangPks.totalgrant));
but when I call the values inside the any html tag it show the value
return (
<Layout title="Borang PKS">
<div>
<p>{pks.borangPks.totalgrant}</p>
<iframe
id="pdf"
type="application/pdf"
style={{ width: '100%', height: '100vh' }}
src="Borang Geran PKS.pdf"
></iframe>
</div>
</Layout>
);
Here the example picture above issue https://i.stack.imgur.com/Xf5zJ.png
I know I'm do something wrong in the async function and what is it?
can someone help this issue. Thank You
I am using Tabulator with React using the react-tabulator module.
I have used 'ref' for the table component and call actions like downloading data or whatever it may be.
import React, { Suspense, useEffect, useReducer, useRef } from 'react';
import PropTypes from 'prop-types';
import "react-tabulator/lib/styles.css"; // default theme
import "react-tabulator/css/tabulator_midnight.min.css";
import {Button } from 'react-bootstrap';
import { ReactTabulator, reactFormatter } from "react-tabulator";
import { reducer } from '../common/reducer';
import ChangeStaffCompetency from './ChangeStaffCompetency';
import * as jsPDF from 'jspdf';
import 'jspdf-autotable';
window.jspdf = jsPDF;
const luxon = require('luxon');
window.DateTime = luxon.DateTime;
// Initial states of StaffCompetency
const initialState = {
changeStaffCompetencyShow: false,
staffID: "",
workflowName: "",
competentTasks: "",
competencyEditorRow: null,
};
const StaffCompetency = (props) => {
// State Handling
const [state, dispatch] = useReducer(reducer, initialState);
// Reference for the tabulator
let staffCompetencyTableRef = useRef(null);
// Action to download workloads data as 'JSON'
const downloadAsJSON = () => {
staffCompetencyTableRef.current.download("json", "RTP_Staff_Competencies.json");
}
/***
* ALL OTHER CODE
*/
return (
<>
<h3 className="text-center"> Staff Competency </h3>
<div>
<Button variant="dark" onClick={() => downloadAsJSON()}>Download JSON</Button>{' '}
</div>
<div style={{clear: 'both'}}></div>
<br></br>
<ReactTabulator
onRef={(r) => (staffCompetencyTableRef = r)}
columns={staffCompetencyTableCoumns}
options={staffCompetencyTableOptions}
/>
<ChangeStaffCompetency
show={state.changeStaffCompetencyShow}
onHide={() => dispatch({ type: "changeStaffCompetencyShow", value: false })}
staffID= {state.staffID}
workflowName= {state.workflowName}
competentTasks= {state.competentTasks}
api={props.api}
parentCallback = {handleCallback}
/>
</>
);
}
StaffCompetency.propTypes = {
api: PropTypes.object.isRequired
};
export default StaffCompetency;
ChangeStaffCompetency is a react-bootstrap modal component which is used as a custom editor to edit the contents of the cell.
staffCompetencyTableRef works fine on the first render but it becomes null on rerendering; for instance when I open and close the ChangeStaffCompetency modal.
How would I resolve this?
Thanks
I solved the issue by changing the type of my useRef variable (staffCompetencyTableRef) to const and used the property of const variables to do my work.
const StaffCompetency = (props) => {
// State Handling
const [state, dispatch] = useReducer(reducer, initialState);
// Reference for the tabulator
const staffCompetencyTableRef = useRef(null);
// Action to download workloads data as 'JSON'
const downloadAsJSON = () => {
staffCompetencyTableRef.current.download("json", "RTP_Staff_Competencies.json");
}
/***
* ALL OTHER CODE
*/
return (
<>
<h3 className="text-center"> Staff Competency </h3>
<div>
<Button variant="dark" onClick={() => downloadAsJSON()}>Download JSON</Button>{' '}
</div>
<div style={{clear: 'both'}}></div>
<br></br>
<ReactTabulator
onRef={(r) => (staffCompetencyTableRef.current = r.current)}
columns={staffCompetencyTableCoumns}
options={staffCompetencyTableOptions}
/>
<ChangeStaffCompetency
show={state.changeStaffCompetencyShow}
onHide={() => dispatch({ type: "changeStaffCompetencyShow", value: false })}
staffID= {state.staffID}
workflowName= {state.workflowName}
competentTasks= {state.competentTasks}
api={props.api}
parentCallback = {handleCallback}
/>
</>
);
}
It kind of feels like a trick. If anyone knows a better approach, please do comment.
Thanks
I have some posts in my database I'm trying to retrieve and edit posts. The posts had some categories which I set as a checkbox. Well, I've retrieved a single post by id successfully but the problem is I also retrieved the categories and I want to show them as checked not all of them only those ones which are set for that particular post. I have another problem I cannot check the box anymore and am not able to add another category to the category list. Help me!
Here is the Edit Post page
import React, { useEffect, useState } from 'react';
import { Alert, Button, Card, Container, Form } from 'react-bootstrap';
import ReactMarkdown from 'react-markdown';
import { useDispatch, useSelector } from 'react-redux';
import { toast, ToastContainer } from 'react-toastify';
import { listCategory } from '../actions/categoryActions';
import { listPostDetails, updatePost } from '../actions/postActions';
const EditPost = ({ history, match }) => {
const postId = match.params.id;
const [categories, setCategories] = useState([]);
const dispatch = useDispatch();
const categoryList = useSelector((state) => state.categoryList);
const { categories: cateList } = categoryList;
const postDetails = useSelector((state) => state.postDetails);
const { post } = postDetails;
useEffect(() => {
if (!userInfo) {
history.push('/login');
}
dispatch(listCategory());
if (!post || post._id !== postId) {
dispatch(listPostDetails(postId));
} else {
setCategories(post.categories);
}
}, [dispatch, history, userInfo, post, postId, categories]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(updatePost(title, desc, img, categories));
history.push('/my_posts');
};
return (
<div className=" createPost mt-4 py-4">
<ToastContainer />
<Container>
<h2>EDIT POST</h2>
<Form onSubmit={submitHandler}>
<Form.Group controlId="category" className="mb-2">
<Form.Label>Select Categories</Form.Label>
<br />
{cateList?.map((cate) => (
<Form.Check
inline
key={cate._id}
type="checkbox"
label={cate.name}
onChange={(e) => {
if (e.target.checked) {
setCategories([...categories, cate.name]);
} else {
setCategories(
categories?.filter((cat) => cat !== cate.name)
);
}
}}
/>
))}
</Form.Group>
<Button
type="submit"
variant="success"
style={{ letterSpacing: '2px', fontWeight: 'bold' }}>
CREATE
</Button>
</Form>
</Container>
</div>
);
};
export default EditPost;
I am making a small blog application using React JS. I am using the context api to store the user's responses globally (in InputContext.js), so that it can be used across different components.
What I want to achieve is, when the user inputs a new blog entry on a separate input page (WriteBlogPost.js) display all the blog entries on a separate page (AllBlogs.js). The page changes are being handled with react router. I have a problem where I am unable to add the new blog objects into the array defined in the context api component (allBlogPosts). I am unsure what is causing this, any explanations and guidance towards the right direction would greatly be appreciated.
InputContext.js
import React, { useState, createContext, useMemo } from 'react'
//create context
export const InputContext = createContext();
const InputContextProvider = (props) => {
const [blogPost, setBlogPost] = useState({
id: '',
title: '',
author: '',
text: ''
});
//create an array to push all the blogPosts
const [allBlogPosts, setAllBlogPosts] = useState([]);
console.log(allBlogPosts)
//put value inside useMemo so that the component only rerenders when there is change in the value
const value = useMemo(() => ({ blogPost, setBlogPost, allBlogPosts, setAllBlogPosts }), [blogPost, allBlogPosts])
return (
<InputContext.Provider value={value}>
{props.children}
</InputContext.Provider>
)
}
export default InputContextProvider;
WriteBlogPost.js
import React, { useState, useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { InputContext } from '../Contexts/InputContext'
import { TextareaAutosize } from '#material-ui/core'
import { v4 as uuidv4 } from 'uuid';
export const WriteBlogPost = () => {
const [blog, setBlog] = useState({
id: '',
title: '',
author: '',
text: ''
});
const history = useHistory();
const { setBlogPost } = useContext(InputContext);
const { allBlogPosts, setAllBlogPosts } = useContext(InputContext)
const handleBlogPost = () => {
setAllBlogPosts(setBlogPost(blog))
history.push("/blogs")
console.log({ blog })
console.log({ allBlogPosts })
}
const handleChange = (e) => {
const value = e.target.value
setBlog({
...blog,
id: uuidv4(),
[e.target.name]: value
})
}
return (
<div>
<label>
Title:
<input type="text" onChange={handleChange} value={blog.title} name="title" />
</label>
<label>
Author:
<input type="text" onChange={handleChange} value={blog.author} name="author" />
</label>
<TextareaAutosize aria-label="minimum height" minRows={20} style={{ width: '70%' }} placeholder="Your blog post"
onChange={handleChange}
value={blog.text}
name="text" />
<div>
<button onClick={handleBlogPost}>Submit</button>
</div>
</div>
)
}
AllBlogs.js(currently unable to map through the array as the array is empty)
import React, { useContext } from 'react'
import { InputContext } from '../Contexts/InputContext'
export const AllBlogs = () => {
const { allBlogPosts } = useContext(InputContext)
console.log(allBlogPosts)
return (
<div>
<h1>All blogs</h1>
{allBlogPosts.map((post) =>
<div>
<p>{post.title}</p>
<p>{post.author}</p>
<p>{post.text}</p>
</div>
)}
</div>
)
}
Just update handleBlogPost
const handleBlogPost = () => {
setBlogPost(blog);
setAllBlogPosts([...allBlogPosts, blog]);
history.push("/blogs");
};
I am fetching details from database and displaying on page and I want to create an edit button which after click can open that details in editable form. In my case that editable form is (EMPLOYEEFORM).
Can you please suggest how to pass id into edit button so the button can take data to edit area.
I am having problem. Right not I have pass id to navlink but its gives me error like employee not found with this id. I am new to reactjs. I tried passing id value but its not acting properly and I am not so aware of passing id into navlink or button. Can you please suggest some direct code or and valuable link where can I update my knowledge.
import React, { useEffect, useState } from 'react';
import './employees.css';
import routePaths from '../../shared/routePaths';
import { getEmployeeDetails } from '../../shared/services/apiService';
import { useParams, NavLink, Redirect } from 'react-router-dom';
import { Descriptions , Card , Divider, Row , Col , Button} from 'antd';
import { isSuccess } from '../../shared/utils/jsHelper';
import { EditTwoTone } from '#ant-design/icons';
const { Meta } = Card;
const employeeDescription = () => {
const {id} = useParams();
const [loading, setLoading] = useState(false);
const [empName, setEmpName] = useState([]);
const [empEmail, setEmpEmail] = useState([]);
const [empPhone, setEmpPhone] = useState([]);
useEffect(() => {
if (id) {
getEmployee();
}
}, [id]);
const getEmployee = () => {
setLoading(true);
getEmployeeDetails(id).then((resp) => {
if (isSuccess(resp)) {
const employee = resp.data.data;
setEmployeeValues(employee);
}
}).finally(() => setLoading(false));
};
const setEmployeeValues = (employee) => {
setEmpName(employee.empName);
setEmpEmail(employee.empEmail);
setEmpPhone(employee.empPhone);
};
return(
<div>
<Card
title="Employee Info"
extra={[
<NavLink to={'${routePaths.EMPLOYEEFORM}/${employee.id}'} className="lin">
<Button key="1">
<EditTwoTone twoToneColor="#000" /> Edit Employee Details
</Button>
</NavLink>,
<NavLink to={routePaths.EMPLOYEES} className="lin">
<Button key="2">
{'<<'} Back to Employee List
</Button>
</NavLink>,
]}
>
<h6>
<strong>Pesonal Details :</strong>
</h6>
<Divider />
<Descriptions className="card-tis">
<Descriptions.Item label="Name ">{empName}</Descriptions.Item>
<Descriptions.Item label="Email ">{empEmail}</Descriptions.Item>
<Descriptions.Item label="Phone ">{empPhone}</Descriptions.Item>
</Descriptions>
</Card>
</div>
);
};
export default employeeDescription;
You can merge all of the states in one state of employee instead of maintaining state for each employee property.
In the provided code you are using signle (') quotes in the navlink instead of backticks. Which will not resolve the variable and you will get a plain string like ${routePaths.EMPLOYEEFORM}/${employee.id}.
I have made few changes please try.
import React, { useEffect, useState } from 'react';
import './employees.css';
import routePaths from '../../shared/routePaths';
import { getEmployeeDetails } from '../../shared/services/apiService';
import { useParams, NavLink, Redirect } from 'react-router-dom';
import { Descriptions, Card, Divider, Row, Col, Button } from 'antd';
import { isSuccess } from '../../shared/utils/jsHelper';
import { EditTwoTone } from '#ant-design/icons';
const { Meta } = Card;
const employeeDescription = () => {
const { id } = useParams();
const [loading, setLoading] = useState(false);
const [employee, setEmployee] = useState({});
useEffect(() => {
if (id) {
getEmployee();
}
}, [id]);
const getEmployee = () => {
setLoading(true);
getEmployeeDetails(id)
.then((resp) => {
if (isSuccess(resp)) {
const employee = resp.data.data;
setEmployee(employee);
}
})
.finally(() => setLoading(false));
};
return (
<div>
<Card
title="Employee Info"
extra={[
<NavLink to={`${routePaths.EMPLOYEEFORM}/${employee.id}`} className="lin">
<Button key="1">
<EditTwoTone twoToneColor="#000" /> Edit Employee Details
</Button>
</NavLink>,
<NavLink to={routePaths.EMPLOYEES} className="lin">
<Button key="2">
{'<<'} Back to Employee List
</Button>
</NavLink>,
]}
>
<h6>
<strong>Pesonal Details :</strong>
</h6>
<Divider />
<Descriptions className="card-tis">
<Descriptions.Item label="Name ">{employee.empName}</Descriptions.Item>
<Descriptions.Item label="Email ">{empEmail.empEmail}</Descriptions.Item>
<Descriptions.Item label="Phone ">{empPhone.empPhone}</Descriptions.Item>
</Descriptions>
</Card>
</div>
);
};
export default employeeDescription;