Upload profile photo to strapi collection - javascript

I have a problem uploading a file that is a picture for a user profile in my user collection in strapi.
This is my photo form in react
import React, {useState} from 'react';
import {Form, Button} from "semantic-ui-react"
import {useFormik} from "formik";
import * as Yup from "yup";
import {toast} from "react-toastify";
import {updatePhotoApi} from "../../../api/user";
export default function ChangeNameForm(props){
const {user, logout,setReloadUser} = props;
const [loading, setLoading] = useState(false);
const formik = useFormik({
initialValues: initialValues(user.photo),
validationSchema: Yup.object(validationSchema()),
onSubmit: async (formData) => {
console.log(formData);
setLoading(true);
const response = await updatePhotoApi(user.id, formData,logout);
console.log(response);
if(!response) {
toast.error("Error al actualizar foto ")
}else{
setReloadUser(true);
toast.success("Foto actualizada");
}
setLoading(false);
}
})
return (
<div className="change-name-form">
<h4>Cambia tu foto</h4>
<Form onSubmit={formik.handleSubmit}>
<Form.Group widths="equal">
<Form.Input
name="photo"
type="file"
placeholder="Tu nueva foto"
error={formik.errors.photo}
onChange={formik.handleChange}
value={formik.values.photo}/>
</Form.Group>
<Button className="submit" loading={loading}>Actualizar</Button>
</Form>
</div>
);
}
function initialValues(photo){
return {
photo: photo || "",
}
}
function validationSchema(){
return {
photo: Yup.string().required(true),
}
}
and there is the api call
export async function updatePhotoApi(idUser,data,logout){
try {
const url = `${BASE_PATH}/users/${idUser}`;
const params = {
method: "PUT",
headers:{
"Content-Type": "application/json"
},
body: JSON.stringify(data),
};
const result = await authFetch(url,params,logout);
console.log(result);
return result ? result : null;
} catch (error) {
console.log(error);
return null;
}
}
but im still getting error 505 internal server error, i dont know what im doing wrong, and cant find such information about to do this.
In my collection for user i have the field called photo for the a single img picture, for later show it as a avatar picture in the client

Related

Unable to post form data using Reactjs

I am working with Reactjs and i am using nextjs,Right now i am trying to Post Form data But Unable to get any response(even anything in console.log),I tried with following code in index.js but nothing works,Thank you in advance
import { NextPage } from "next";
import Head from "next/head";
import Image from "next/image";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import Link from 'next/link'
import Script from 'next/script'
import ReactDOM from 'react-dom';
import axios from "axios";
//const Home: NextPage = () => {
const Home = () => {
const [state, setState] = useState({
name: "",
job: ""
});
const handleChange = (e) => {
console.log('Om Success')
const value = e.target.value;
setState({
...state,
[e.target.name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("Om Success");
const userData = {
name: state.name,
job: state.job
};
axios.post("https://xxxxxxx.com/api/users", userData).then((response) => {
console.log(response.status);
console.log(response.data);
});
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="name">
Name
<input
type="text"
name="name"
value={state.name}
onChange={handleChange}
/>
</label>
<label htmlFor="job">
Job
<input
type="text"
name="job"
value={state.job}
onChange={handleChange}
/>
</label>
<button type="submit">Register</button>
</form>
</>
);
};
export default Home;
you can post axios data by using FormData() like:
const formData= new FormData();
And then add the fields to the form you want to send:
formData.append('name', state.name);
formData.append('job', state.job);
axios({
method: "post",
url: "your url",
data: formData,
}).then((response) => {
console.log(response.status);
console.log(response.data);
});

Update profile picture in react

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>
)

Unable to send image from frontend to cloudinary or the backend in general

I am trying to send an image from the frontend to cloudinary to be used as profile picture in my app using the code below:
import { StyledInput, StyledLabel} from '../FormInputs/styles'
import { useState } from 'react'
import { StyledButton } from '../Button/styles'
import axios from 'axios'
export function FileUploader() {
const [file, setFile] = useState(null)
const [image, setImage] = useState(null)
const [showImage, setShowImage] = useState()
function handleChange(e) {
readFile(e.target.files[0])
setFile(e.target.files[0])
console.log(e.target.files[0])
}
function readFile(file) {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = e => setImage(e.target.result)
reader.onerror = e => console.log(reader.error)
}
async function handleSubmit(e) {
e.preventDefault()
const token = localStorage.getItem('token')
const data = new FormData()
if(file) {
data.append('file', file[0], file[0].name)
}
const response = await axios({
method: 'PUT',
base: 'http://localhost:8000',
url: '/clients/clientprofile',
data,
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': `Bearer ${token}`
}
})
console.log(response)
// setShowImage(response.image)
}
return (
<div>
<form onSubmit={handleSubmit}>
<StyledLabel htmlFor="file">Elegir foto de perfil</StyledLabel>
<StyledInput
type="file"
accept="image/*"
name="file"
id="file"
onChange={handleChange}
/>
<StyledButton type="submit">Enviar Foto</StyledButton>
</form>
{image && <img src={image} alt="Profile Picture Preview" />}
</div>
)
}
When I select the image it works fine, however, when I click the "Upload Image" button, it errors out with the below error:
I am defining file by using the setFile(), however, when I console.log(file) it returns null
Since the console.log() is async, it was logging null because it was executing before the state had time to change, and the issue with the undefined was due to an error of the config in the backend.

Can't figure out how to skip query until api request is ready so it doesnt return a undefined variable in apollo useQuery with useSWR

I have the following code which hits a user api with useSWR, if I console log the user the first two times it renders undefined. useQuery is complaingng user.id is undefined which is true at some point in the render, however I have tried to pass a skip option and it works with passing a skip option for the cookie variable which has a similar problem, either I don't know how to pass two variables to skip, or it can only take one, or it's not working all together.
Here is my code thus far
import Layout from "../components/Layout";
import { useForm } from "react-hook-form";
import useSWR from "swr";
import { gql, useQuery } from "#apollo/client";
import { useState, useEffect } from "react";
export const CATALOG_VALUES = gql`
query GetCatalogValues($id: ID!) {
findUserByID(id: $id) {
catalog {
decor
clothing
supplies
furniture
_id
}
email
}
}
`;
const fetcher = (url) => fetch(url).then((r) => r.json());
export default function Welcome() {
const { data: user, error: userError } = useSWR("/api/user");
const { data: cookieData, error: cookieError } = useSWR(
"/api/cookie",
fetcher
);
var cookieBearer = `Bearer ${cookieData}`;
const { loading, error, data } = useQuery(CATALOG_VALUES, {
variables: { id: user.id },
context: {
headers: {
authorization: cookieBearer,
},
},
skip: !cookieBearer || !user, // I have tried many different things here
});
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => console.log(data);
console.log(errors);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error.message}</p>;
if (cookieError) return <p>Cookie Error</p>;
if (!user) return <p>loading</p>;
if (userError) return <div>failed to load</div>;
return (
<Layout>
<h1>Add your Catalog Type Below</h1>
{user.id}
<form onSubmit={handleSubmit(onSubmit)}>
<h2>Decor</h2>
<input
name="Add Form"
type="radio"
value="Decor"
ref={register({ required: true })}
/>
<div>
<input type="submit" />
</div>
</form>
</Layout>
);
}
Creating a componenet and passing props fixed it
const Form = ({ cookieBearer, user }) => {
const { loading, error, data } = useQuery(CATALOG_VALUES, {
variables: { id: user.id },
context: {
headers: {
authorization: cookieBearer,
},
},
});
console.log(data);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error.message}</p>;
return <p>hello world</p>;
};
{cookieBearer && user && <Form cookieBearer={cookieBearer} user={user} />}

multer/filepond upload returning undefined

i am currently trying to integrate multiple filepond components/instances into my react app, where the images are uploaded on button/form submit.
I have it more or less working with the code below, but i'm getting an undefined result when logging out the results in routes.js file, even though when I log out the state on submit in upload.js, i do get results.
I've tried logging out just req.files, it returns undefined, the method i've used below comes directly from multers documentation and this logs out - TypeError: Cannot read property '0' of undefined
Thanks
upload.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import "./styles.css";
import { FilePond, registerPlugin } from "react-filepond";
import "filepond/dist/filepond.min.css";
// import FilePondPluginFileEncode from 'filepond-plugin-file-encode';
import FilePondPluginImageExifOrientation from "filepond-plugin-image-exif-orientation";
import FilePondPluginImagePreview from "filepond-plugin-image-preview";
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css";
// Register the plugins
registerPlugin(FilePondPluginImageExifOrientation, FilePondPluginImagePreview);
const API_BASE = "http://localhost:5000";
function submitForm(contentType, data, setResponse) {
axios({
url: `${API_BASE}/upload`,
method: "POST",
data: data,
headers: {
"Content-Type": contentType
}
})
.then(response => {
setResponse(response.data);
})
.catch(error => {
setResponse("error");
});
}
function App() {
const [title, setTitle] = useState("");
const [file, setFile] = useState("");
const [file3, setFile3] = useState("");
const [desc, setDesc] = useState("");
function uploadWithFormData() {
const formData = new FormData();
formData.append("title", title);
formData.append("file", file);
formData.append("file3", file3);
formData.append("desc", desc);
submitForm("multipart/form-data", formData, msg => console.log(msg));
}
return (
<div className="App">
<h2>Upload Form</h2>
<form>
<label>
File Title
<input
type="text"
vaue={title}
onChange={e => {
setTitle(e.target.value);
}}
placeholder="Give a title to your upload"
/>
</label>
<FilePond
name={file}
files={file}
allowMultiple={false}
server={null}
instantUpload={false}
onupdatefiles={setFile}
/>
<FilePond
name={file3}
files={file3}
allowMultiple={false}
server={null}
instantUpload={false}
onupdatefiles={setFile3}
/>
<label>
Description
<textarea value={desc} onChange={e => setDesc(e.target.value)} />
</label>
<input
type="button"
value="Upload as Form"
onClick={uploadWithFormData}
/>
</form>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
routes.js
router.post('/', upload.fields([{ name: 'file'}, { name: 'file3' }]), (req, res) => {
console.log(req.files['file'][0]);
console.log(req.files['file3'][0]);
var movieData = {
desc: req.body.desc,
title: req.body.title,
imgCollection: req.files['file'],
poster: req.files['file3']
};
Movie.create(movieData)
.then(movie => res.json({ msg: 'Movie added successfully' }))
.catch(err => res.status(400).json({ error: 'Unable to add this movie' }));
});
image of console log
console log upload.js
What you're loggin is the FilePond file item, it's not a File object. The file item has a file property that contains the actual file object, if the array you logged is what you're posting to the server you need to map it so it contains file objects instead of FilePond file items.
You should not pass any Content-type in headers.
Get file property from the files while appending like below,
formData.append("file", files.file);

Categories