I am trying to trouble shoot/fix my nodejs upload image api:
My service is being stuck at somewhere.
My service is too simple, just uploading the image by resizing through sharp api in a directory and return the full path of that file.
When I select some image at first time then everything works fine and image upload successfully with a response
but When I try to crop that image after clicking on the image and try to save it (at second time) then nodejs service return following response.
I don't why it is being happened, I tried to debug the service code and It didn't stop at anywhere. Flow has been passed/executed successfully, I didn't catch any exception/error in code.
What can be the issue in it because it still displaying
> Blockquote failed to load response data. no resource with given identifier found
Problem area is in the code of onSaveImage when a server side call is being served/requested. I am using the plugin for image cropping is react-easy-crop. Browser is getting refresh/reload at this line of code
const jsonRes = await responseMain.json();
I am sharing my nodejs service code as well as reactjs code. please look into it.
Thank you.
-----------------package.json of nodejs
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-validator": "^6.12.0",
"handlebars": "^4.7.7",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"nodemailer": "^6.6.1",
"nodemon": "^2.0.12",
"sharp": "^0.29.3"
},
"devDependencies": {},
"scripts": {
"start": "nodemon --inspect index.js",
"debug": "node --nolazy --inspect-brk=9229 index.js"
},
"license": "ISC"
}
------ index.js---------------------------NodeJs
const express = require("express");
const app = express();
const cors = require("cors");
var fs = require("fs");
const fsp = fs.promises;
var path = require("path");
const sharp = require("sharp");
var $pathContentBuilder = path.join(__dirname, "../", "/public/roughdata/uploads/");
var $urlpathContentBuilder = "/roughdata/uploads/"; // URL path
app.use(express.json({ limit: "20mb" }));
app.use(cors());
app.use(
express.urlencoded({
extended: true,
})
);
function processImage(image, metadata, filename, isResizeAllow) {
return new Promise((resolve, reject) => {
if (isResizeAllow && isResizeAllow === true) {
// 0.8 MB
return image
.resize({
width: Math.round(metadata.width / 2),
height: Math.round(metadata.height / 2),
fit: "cover",
})
.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
} else {
return image.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
}
});
}
app.post("/uploaddetail", async (req, res, next) => {
const base64Data = req.body.image;
const filename = req.body.filename;
let imageResizeResult = "";
try {
var inputbuf = Buffer.from(base64Data, "base64"); // Ta-da
const image = await sharp(inputbuf);
let metadata = await image.metadata();
let convertedbase64Data = "";
convertedbase64Data = await processImage(image, metadata, filename, false);
await fsp.writeFile($pathContentBuilder + filename, convertedbase64Data, "base64");
let resultResponse = JSON.stringify({
success: true,
fullurl: `${$urlpathContentBuilder}${filename}`,
url: `${filename}`,
imagename: `${filename}`,
serverpath: `${$urlpathContentBuilder}`,
});
//res.type("json");
res.status(200).json(resultResponse);
//res.end();
//next();
} catch (e) {
console.log(e);
const error = new HttpError(e, 404);
return next(error);
}
});
and following is my reactjs code.
import React, { useState, useEffect, useCallback } from "react";
import { withRouter, useParams, useHistory } from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import Page from "./Page";
import TitleBar from "./TitleBar";
import SingleGalleryRow from "./SingleGalleryRow";
import CenteredPopup from "../common/CenteredPopup";
import { MdDelete, MdOutlineCrop, MdOutlineCircle } from "react-icons/md";
import Cropper from "react-easy-crop";
import getCroppedImg from "../shared/util/cropImage";
import b64toBlob from "../shared/util/blobToImage";
function EditImage({ breadcrumb, pageName, setLoader, showToast }) {
const [list, setList] = useState([]);
const history = useHistory();
const [umodalShow, setUModalShow] = useState(false);
const [row, setRow] = useState({
ImageName: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
Name: "",
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
const [isSubmitClicked, setIsSubmitClicked] = useState(false);
const [IsDisabled, setIsDisabled] = useState(false);
const [nameError, setNameError] = useState("");
const [validated, setValidated] = useState(0);
const [ImageName, setImageName] = useState("");
const [base64SelectedImage, setBase64SelectedImage] = useState("");
const { id } = useParams();
let content = "";
const [detail, setDetail] = useState({
Content: "",
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
Name: "",
SiteId: 1,
});
useEffect(() => {
let abortController = new AbortController();
let mounted = true;
const fetchData = async () => {
try {
setLoader(true);
let apiUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/getbyId/" + id;
const response = await fetch(apiUrl, {
signal: abortController.signal,
});
const json = await response.json();
const resultPages = json ? json.list : "";
if (resultPages && resultPages.length > 0) {
//debugger;
let firstRow = resultPages[0];
let isEnabled = false;
if (firstRow.IsVisible === 1 || firstRow.IsVisible === true) {
isEnabled = true;
}
let newDetailPage = {
Id: firstRow.Id,
Name: firstRow.Name,
Content: firstRow.Content
};
if (mounted) {
setDetail(newDetailPage);
}
let itemRows = [];
for (let item of resultPages) {
let row = {
Id: item.DetailId,
ImageSliderId: item.ImageSliderId,
ImageName: item.ImageName,
ImageFullUrl: item.ImageFullUrl,
};
if (item.ImageFullUrl) {
itemRows.push(row);
}
}
if (itemRows && itemRows.length > 0) {
if (mounted) {
setList(itemRows);
}
}
}
setLoader(false);
//abortController = null;
} catch (err) {
setLoader(false);
if (err.name === "AbortError") {
// Handling error thrown by aborting request
}
}
};
fetchData();
// Side-effect logic...
return () => {
// Side-effect cleanup
mounted = false;
abortController.abort();
console.log("unmounting...");
};
}, []);
const [imageCropWidth, setImageCropWidth] = useState(100);
const [imageCropHeight, setImageCropHeight] = useState(100);
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [aspect, setAspect] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const [croppedImage, setCroppedImage] = useState(null);
const [cropSize, setCropSize] = useState({ width: 100, height: 100 });
const [Image, setImage] = useState("");
const [rotation, setRotation] = useState(0);
const [cropShape, setCropShape] = useState("rect");
const onSaveImage = useCallback(
async (e) => {
e.preventDefault();
if (validate() < 0) {
return -1;
}
try {
let filename = "";
let base64Image = "";
let blobUrl = "";
debugger;
const cropImage = await getCroppedImg(Image, croppedAreaPixels, rotation);
setCroppedImage(cropImage);
setImage(cropImage);
base64Image = cropImage ? cropImage.replace(/^data:image\/(png|jpeg);base64,/, "") : "";
const contentType = "image/jpeg";
if (cropImage) {
debugger;
setRow((prevState) => {
return {
...prevState,
["ImageFullUrl"]: cropImage,
["ImageName"]: ImageName ? ImageName : "",
};
});
filename = ImageName ? ImageName : "";
const settings = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: filename,
image: base64Image,
}),
};
setLoader(true);
let submitUrl = process.env.REACT_APP_SERVER_DOMAIN + `/uploaddetail`;
const responseMain = await fetch(submitUrl, settings);
const jsonRes = await responseMain.json();
if (jsonRes) {
let convertedJson = jsonRes;
if (convertedJson) {
setLoader(false);
const uploadedImageUrl = convertedJson.fullurl; // get saved image url
const uploadedImageName = convertedJson.imagename;
const uploadedServerPath = convertedJson.serverpath;
const uploadurl = convertedJson.url;
setImageName(convertedJson.imagename);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: uploadedImageName,
["ImageServerPath"]: uploadedServerPath,
["ImageFullUrl"]: uploadedImageUrl,
["ImageUrl"]: uploadurl,
};
});
if (uploadedImageUrl) {
setIsDisabled(false);
}
setUModalShow(false);
setDetail((prevState) => {
return {
...prevState,
["Content"]: content,
};
});
}
}
}
} catch (err) {
console.log(err);
showToast("Error!", "Image couldn't be saved");
} finally {
}
},
[croppedAreaPixels, rotation]
);
const showImageForCrop = useCallback(
async (e) => {
debugger;
let localSrc = e.ImageFullUrl; //e.target.src;
setImage(localSrc);
setUModalShow(true);
setImageName(e.ImageName);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id === e.Id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
},
[row]
);
const singlefileSelectedHandler = async (e) => {
//debugger;
e.preventDefault();
setIsDisabled(true);
content = "";
if (!e.target.files || e.target.files.length <= 0) {
return -1;
}
if (detail && detail.Content) {
content = detail.Content;
}
//let imageList = "";
const selectedImage = e.target.files[0];
let imageSizeInMBs = selectedImage.size ? selectedImage.size / 1024 / 1024 : 0;
if (selectedImage && imageSizeInMBs > 8) {
setIsDisabled(false);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: "",
["ImageServerPath"]: "",
["ImageFullUrl"]: "",
["ImageUrl"]: "",
};
});
showToast("Information!", "Image size can't be greater than 8MB.");
return -1;
}
const filename =
selectedImage && selectedImage.name
? new Date().valueOf() +
"_" +
selectedImage.name.replace(/\\/g, "").replace(/ /g, "").replace(/'/g, "").replace(/"/g, "").replace(/`/g, "")
: "";
setUModalShow(false);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
setImageName(filename);
setUModalShow(true);
let convertedImageFile = await convertImage(selectedImage);
setBase64SelectedImage(convertedImageFile);
};
async function convertImage(file) {
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
e.preventDefault();
let base64 = e.target.result;
base64 = base64.replace(/^data:image\/(png|jpeg);base64,/, "");
setImage(reader.result);
resolve(base64);
//resolve(reader.result);
};
reader.onerror = function (error) {
console.log("Error: ", error);
reject(error);
};
});
}
const onAddSide = (e) => {
//debugger;
let newImageList = [...list];
if (row.Id && row.Id > 0) {
if (newImageList && newImageList.length > 0) {
const selectedIndex = newImageList.findIndex((item) => item.Id === row.Id);
if (selectedIndex > -1) {
newImageList[selectedIndex] = row;
}
}
} else {
row.Id = Math.max.apply(
Math,
newImageList.map(function (o) {
return o.Id + 1;
})
);
if (!row.Id || (row.Id && row.Id === 0) || row.Id === -Infinity) {
row.Id = 1;
}
newImageList.push(row);
}
setList(newImageList);
};
const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
//debugger;
setCroppedAreaPixels(croppedAreaPixels);
}, []);
const onClose = useCallback(() => {
setCroppedImage(null);
}, []);
const onCropperZoomChange = (e) => {
if (e && e.currentTarget && e.currentTarget.value) {
let lZoomValue = e.currentTarget.value;
if (lZoomValue && lZoomValue < 0.4) {
//setZoom(0.28);
setZoom(0.4);
} else if (lZoomValue && lZoomValue > 1) {
setZoom(1);
} else {
setZoom(lZoomValue);
}
}
};
return (
<Page breadcrumb={breadcrumb} pageName={pageName} fluid>
<form noValidate validated={validated} onSubmit={HandleSaveMenu} className="user-form">
<Card>
<Card.Header>
<TitleBar title="Edit Gallery" />
</Card.Header>
<Card.Body>
<div className="form-row">
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Width
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropWidth}
onChange={(e) => setImageCropWidth(e.target.value)}
/>
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Height
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropHeight}
onChange={(e) => setImageCropHeight(e.target.value)}
/>
</div>
</div>
<SingleGalleryRow
data={row}
setRow={setRow}
onUpload={singlefileSelectedHandler}
onAddSide={onAddSide}
IsDisabled={IsDisabled}
/>
<ul className="slide-list">
{list &&
list.map(function (item, i) {
//debugger;
return (
<li key={i}>
<div className="slide-row">
<div className="img-holder">
<img
className="thumbnail"
src={item.ImageFullUrl}
alt={item.ImageName}
onClick={() => showImageForCrop(item)}
/>
</div>
</div>
</li>
);
})}
</ul>
</Card.Body>
<Card.Footer>
<div className="btn-holder text-right">
<Button type="submit" variant="primary" disabled={isSubmitClicked}>
Save
</Button>
</div>
</Card.Footer>
</Card>
</form>
<CenteredPopup
show={umodalShow}
onHide={() => setUModalShow(false)}
title="Crop Image"
content=""
closetext="Close"
savetext="Apply"
onSaveChanges={onSaveImage}
>
<div className="cropimage-container">
<div className="cropimage-tools">
<span className="cropimage-icons" onClick={(e) => setCropShape("rect")}>
<MdOutlineCrop />
<span className="cropimage-text">Crop</span>
</span>
<span className="cropimage-icons" onClick={(e) => setCropShape("round")}>
<MdOutlineCircle />
<span className="cropimage-text">Circle</span>
</span>
</div>
<div className="crop-container">
<Cropper
image={Image}
crop={crop}
cropSize={cropSize}
cropShape={cropShape}
zoom={zoom || 1}
showGrid={true}
zoomWithScroll={false}
objectFit="horizontal-cover"
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={onCropperZoomChange}
/>
</div>
</div>
<div className="form-row ic_rangeslider">
<div className="form-group col-md-12">
<label className="form-label" htmlFor="ZoomBtn">
{zoom || 1}
</label>
<span className="range-slider__wrap">
<input
id="zoomrangeid"
type="range"
className="slider range-slider range-slider--light"
value={zoom || 1}
min={0.4}
max={1}
step={0.1}
aria-labelledby="Zoom"
onChange={onCropperZoomChange}
/>
</span>
</div>
</div>
</CenteredPopup>
</Page>
);
}
export default withRouter(EditImage);
Related
I'm having an issue with ZXing-js. Its returning a ChecksumException no matter what QR Code I put into it. So its detecting the QRCode but throws an Exception. The following is my vuejs 2.0 code. Please help.
Almost all if this code works. Its just the reading of the QR code part that doesn't
import { BarcodeFormat, DecodeHintType, NotFoundException, ChecksumException, FormatException } from '#zxing/library';
const ZXing = require('#zxing/browser');
Vue.component('qr_scanner_modal',{
prop: [
'videoSource'
],
data: function ()
{
return {
qr_error: null,
qrcanvas: null,
context: null,
qrvideo: null,
hints: null,
formats: null,
videoSource: {},
qr: null,
selected_source: null,
polling: null,
localMediaStream: null,
scanLineDirect: 'down',
scanlineOffset: 0,
qr_title: "",
visible: false,
focused: true,
qr_result: ""
};
},
mixins: [ focusMixin ],
created: function ()
{
EventBus.$on('trigger-qrcode-scanner', (qrTitle) => this.show(qrTitle));
},
mounted: function ()
{
let self = this;
this.$root.$on('bv::modal::show', () => this.$nextTick(() => this.mountQRReader()));
},
methods: {
mountQRReader: function ()
{
const hints = new Map();
const formats = [BarcodeFormat.QR_CODE, BarcodeFormat.DATA_MATRIX/*, ...*/];
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
hints.set(DecodeHintType.TRY_HARDER, true);
hints.set(DecodeHintType.CHARACTER_SET, 'UTF-8');
hints.set(DecodeHintType.ALSO_INVERTED, true);
this.qr = new ZXing.BrowserQRCodeReader(hints);
this.qrcanvas = this.$refs['qrcanvas'];
this.qrcanvas.width = 400;
this.qrcanvas.height = 400;
// this.qrcanvas = this.$refs;
console.log(this.$refs['qrcanvas']);
this.context = this.$refs['qrcanvas'].getContext('2d');
this.qrvideo = this.$refs['qrvideo'];
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices)
{
this.qr_error = "This browser does not support MediaStreamTrack. Try Chrome.";
console.log("enumerateDevices() not supported.");
}
else
{
this.qr_error = null;
}
let self = this;
navigator.mediaDevices
.enumerateDevices()
.then(function (sourceInfos)
{
let videosource = [];
for (var index = 0; index !== sourceInfos.length; index++)
{
var sourceInfo = sourceInfos[index];
if (sourceInfo.kind === 'videoinput')
{
videosource.push({
id: sourceInfo.deviceId,
name: sourceInfo.label || 'Camera ' + index
});
console.log(sourceInfo);
console.log(videosource);
}
}
self.videoSource = videosource;
})
.catch(function (err)
{
console.log(err.name + ": " + err.message);
});
},
show: function (qrTitle)
{
console.log("Show modal called.");
this.qr_title = qrTitle + " - QR / Magstripe Reader";
this.$bvModal.show('qr_code_scanner');
},
dismiss: function()
{
this.stopScan();
this.$bvModal.hide('qr_code_scanner');
},
selectSource: function (source)
{
this.selected_source = source;
let constraints = {
audio: false,
video: {
facingMode: "environment",
sourceId: source
}
};
navigator.getUserMedia(constraints, this.startScan, this.scanError);
},
read: function (value)
{
console.log('read callback called.');
console.log(value);
if (value !== null && value !== undefined)
{
this.qr_result = value.text;
EventBus.$emit('qr_code_returned', value.text);
this.stopScan();
return;
}
},
startScan: function (stream)
{
this.qrvideo.srcObject = stream;
this.localMediaStream = stream;
this.qrvideo.play();
this.polling = setInterval(this.scan, 400);
},
scanError: function (err)
{
if (err)
{
this.qr_error = err;
}
},
stopScan: function ()
{
clearInterval(this.polling);
if (this.localMediaStream)
{
let track = this.localMediaStream.getVideoTracks();
track[0].stop();
}
},
transposeRect: function (width, height)
{
const rectWidth = width * 0.8;
const rectHeight = height * 0.8;
const xPos = (width - rectWidth) / 2;
const yPos = (height - rectHeight) / 2;
this.context.beginPath();
this.context.strokeStyle = 'red';
this.context.lineWidth = '3';
this.context.rect( xPos,
yPos,
rectWidth,
rectHeight);
this.context.stroke();
this.drawScanLine(yPos,
xPos,
xPos + rectWidth,
yPos + rectHeight);
},
drawScanLine: function (top, left, right, bottom)
{
if (this.scanLineDirect === 'down')
{
this.scanlineOffset = this.scanlineOffset + 4;
}
if (this.scanLineDirect === 'up')
{
this.scanlineOffset = this.scanlineOffset - 4;
}
if (top + this.scanlineOffset > bottom)
{
this.scanLineDirect = 'up';
this.scanlineOffset = this.scanlineOffset - 4;
}
if (top + this.scanlineOffset < top)
{
this.scanLineDirect = 'down';
this.scanlineOffset = this.scanlineOffset + 4;
}
this.context.beginPath();
this.context.strokeStyle = 'red';
this.context.lineWidth = '3';
this.context.moveTo(left, top + this.scanlineOffset);
this.context.lineTo(right, top + this.scanlineOffset);
this.context.closePath();
this.context.stroke();
},
scan: async function ()
{
try
{
if (this.localMediaStream)
{
console.log("Scanning Video Feed.");
const width = this.qrcanvas.getAttribute('width');
const height = this.qrcanvas.getAttribute('height');
this.context.drawImage(this.qrvideo, 0, 0, width, height);
console.log("width: " + width);
console.log("height: " + height);
const code = await this.qr.decodeFromCanvas(this.qrcanvas);
// const code = await this.qr.decode(this.qrcanvas);
this.read(code);
this.transposeRect(width, height);
}
}
catch(err)
{
if (err instanceof NotFoundException) {
console.log('No QR code found.')
}
if (err instanceof ChecksumException) {
console.log('A code was found, but it\'s read value was not valid.')
}
if (err instanceof FormatException) {
console.log('A code was found, but it was in a invalid format.')
}
}
}
},
template: `
<b-modal id="qr_code_scanner"
v-bind:title="qr_title"
v-bind:videoSource='videoSource'
hide-footer >
<div class="alert alert-danger" v-show="qr_error != null">
{{qr_error}}
</div>
<div>
<a href='#' v-for="source in videoSource" v-on:click='selectSource(source.id)' class='btn btn-primary'>#{{source.name}}</a>
</div>
<div class="large-centered col-lg-12 col-md-12 col-sm-12" style="overflow: hidden;">
<input type="text" ref="qr_result" name='qr_result' v-focus="focused" v-model='qr_result' class="form-control" />
<video id="qrvideo" ref="qrvideo" controls="false" style="display: none;"></video>
<canvas id="qrcanvas" ref='qrcanvas' style="overflow: hidden;" width="400" height="400"></canvas>
</div>
<div class="modal-footer">
<a href='#' v-on:click='dismiss()' class='btn btn-primary'>Cancel</a>
</div>
</b-modal>
`
});
I'm expecting it to return a QR Code.
When testing the API I have noticed that it is getting called around 6 times, there is no for/foreach loop that would make it run several times in the code so am unsure as to why this may be happening.
The API runs every time a user goes onto the Landing Screen.
Any advice would be appreciated
exports.getFilteredOpportunities = async (req, res) => {
let mongoQuery = generateMongoQuery(req);
try {
const opps = await Opportunity.find(mongoQuery)
.select(removeItems)
.sort({
createdAt: -1
});
res.status(200).json({
opportunities: opps,
});
} catch (error) {
res.status(500).json({
status: "error",
message: error,
});
}
};
GenerateMongoQuery function
const generateMongoQuery = (req) => {
let query = {};
// search param used searching through Title field, ie. search=food
if (req.query.search && req.query.search.length > 0) {
query.$or = [{}, {}];
query.$or[0].Title = query.$or[1].Description = new RegExp(
`${req.query.search}`,
"i"
);
}
// location param, i.e location=Glasgow,Manchester
if (req.query.location && req.query.location.length > 0) {
query.location = {
$in: req.query.location.split(",")
};
}
// timeslot param, i.e timeslot=Evening,Morning
if (req.query.timeslot && req.query.timeslot.length > 0) {
query.timeslot = {
$in: req.query.timeslot.split(",")
};
}
// category param, returning corresponding id i.e category=
if (req.query.category && req.query.category.length > 0) {
query.category = {
$in: req.query.category.split(",")
};
}
// Dont load expired opportunities
query.eventDate = {
$gte: new Date().toDateString()
};
// Only return non-cancelled opportunities
query.isCancelled = false;
return query;
};
The landing Screen
import { useState, useEffect } from "react";
import Opportunities from "../components/molecules/Opportunities";
import Filters from "../components/organisms/Filters";
import { IOpportunity } from "../Models/IOpportunity";
import OpportunitiesClient from "../Api/opportunitiesClient";
import Utils from "../Utils/Utils";
import FiltersClient from "../Api/filtersClient";
import { IFilters } from "../Models/IFilters";
import { ISelectedFilters } from "../Models/ISelectedFilters";
import Header from "../components/atoms/Header";
import Footer from "../components/atoms/Footer";
export default function LandingScreen(props) {
const [opportunities, setOpportunities] = useState<IOpportunity[]>([]);
const [filters, setFilters] = useState<IFilters[]>([]);
const [checkedFilters, setCheckedFilters] = useState<
ISelectedFilters | undefined
>({
Location: [],
Category: [],
Timeslot: [],
});
const [isLoading, setLoading] = useState<boolean>(true);
const [allResultsLoaded, setAllResultsLoaded] = useState<boolean>(false);
let pageToGet = 1;
const [scrollPosition, setScrollPosition] = useState(0);
const [totalOpps, setTotalOpps] = useState(0);
useEffect(() => {
getOpportunities();
getFilters();
}, []);
useEffect(() => {
setTotalOpps(opportunities.length);
}, [opportunities]);
const handleScroll = () => {
setScrollPosition(window.pageYOffset);
let scrollHeight = 0;
if ((props.scrollHeight === null) !== undefined) {
scrollHeight = +props.scrollHeight;
} else {
scrollHeight = document.scrollingElement.scrollHeight;
}
if (
window.innerHeight + document.documentElement.scrollTop >=
scrollHeight
) {
if (allResultsLoaded === false) {
setLoading(true);
}
setTimeout(() => {
pageToGet += 1;
getOpportunities();
}, 600);
}
window.removeEventListener("scroll", handleScroll);
};
window.addEventListener("scroll", handleScroll, { passive: true });
const setItemChecked = (filtername: string, filterType: string) => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
setCheckedFilters(
Utils.setItemChecked(filtername, filterType, checkedFilters)
);
};
const resetFilters = () => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
checkedFilters.Location = [];
checkedFilters.Category = [];
checkedFilters.Timeslot = [];
getOpportunities();
};
const getFilters = () => {
FiltersClient.getAll().then((filters) => {
setFilters(filters);
});
};
const getOpportunities = () => {
console.log(opportunities);
OpportunitiesClient.getWithParams(
Utils.getAxiosParams(checkedFilters)
).then((res) => {
definePage(res);
if (opportunities.length === res["opportunities"].length)
setAllResultsLoaded(true);
setLoading(false);
});
};
const definePage = (res) => {
if (pageToGet === 1) {
setOpportunities(res["opportunities"].slice(0, 15));
} else {
setOpportunities(
opportunities.concat(
res["opportunities"].slice(
opportunities.length,
opportunities.length + 15
)
)
);
}
};
return (
<div>
<Header page="landing" includePostButton={true} />
<div
data-testid="test-div1"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4 mb-5 my-10"
>
<div></div>
<div className="card-container-title">
{totalOpps} Results{" "}
{checkedFilters.Location.length > 0 ? "(Filtered)" : ""}
</div>
</div>
<div
data-testid="test-div3"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4"
>
<div>
<Filters
filters={filters}
getChecked={setItemChecked}
urlCall={getOpportunities}
resetFilters={resetFilters}
checkedFilters={checkedFilters}
/>
</div>
<div
data-testid="test-div4"
className={isLoading ? "opacity-50" : ""}
>
<div className="col-span-2">
<Opportunities
items={opportunities}
isLoading={isLoading}
allResultsLoaded={allResultsLoaded}
/>
</div>
</div>
</div>
<Footer />
</div>
);
}
I have a react application built for production and being served by a node/express server.
I have started the server locally and also deployed it to heroku and render.com
On all these systems, Chrome and Edge have no problems and no errors when accessing the site. But: Firefox won't populate the root element and shows a weird error. Only after refreshing, Firefox shows the site perfectly.
This is the error and the corresponding code ... it doesn't make sense
TypeError: wi.get(...) is undefined
ts Header.jsx:36
React 7
C scheduler.production.min.js:13
T scheduler.production.min.js:14
813 scheduler.production.min.js:14
Webpack 12
react-dom.production.min.js:189:29
React 9
C scheduler.production.min.js:13
T scheduler.production.min.js:14
(Async: EventHandlerNonNull)
813 scheduler.production.min.js:14
Webpack 12
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
^---- The error is showing for this code position! There is no code!
country_code: "de",
},
];
This happens on all three environments, tested from three different systems.
Does anyone know what is happening?
EDIT: Full Header.jsx
import { useState, useRef, useEffect } from "react";
import i18next from "i18next";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import useOutsideClick from "../hooks/useOutsideClick";
import useAuth from "../hooks/useAuth";
import Anchor from "./Anchor";
import "./Header.css";
import claryNextLogo from "../images/ClaryNext2.png";
import cookies from "js-cookie";
import { config } from "../Environment";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { solid } from "#fortawesome/fontawesome-svg-core/import.macro";
import { useContext } from "react";
import { WSContext } from "../App";
import { useEffectOnce } from "../hooks/useEffectOnce";
import { distributionService } from "../services/distributionService";
import MessageBox from "./MessageBox";
import useRetryFetch from "../hooks/useRetryFetch";
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
country_code: "de",
},
];
function Header() {
const currentLanguageCode = cookies.get("i18next").split("-")[0] || "en";
const currentLanguage = availableLanguages.find(
(l) => l.code === currentLanguageCode
);
const { auth, setAuth } = useAuth();
const navigate = useNavigate();
const dropdownRef = useRef(null);
const retryFetch = useRetryFetch();
const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
const [showMessageBox, setShowMessageBox] = useState(false);
const [msgBoxTitle, setMsgBoxTitle] = useState("");
const [msgBoxButtons, setMsgBoxButtons] = useState({});
const [msgBoxInputs, setMsgBoxInputs] = useState([]);
const [msgBoxId, setMsgBoxId] = useState("");
const [msgBoxMoreJSX, setMsgBoxMoreJSX] = useState(null);
const [chatRequestUser, setChatRequestUser] = useState("");
const [chatRequestProcessId, setChatRequestProcessId] = useState("");
const [chatRequestRoomId, setChatRequestRoomId] = useState(0);
const { t } = useTranslation();
const { socket } = useContext(WSContext);
const openMessageBox = (title, id, buttons, inputs, moreJSX) => {
setMsgBoxTitle(title);
setMsgBoxId(id);
setMsgBoxButtons(buttons);
setMsgBoxInputs(inputs);
setMsgBoxMoreJSX(moreJSX);
setShowMessageBox(true);
};
const onButton = async (result) => {
console.log("MessageBox button was clicked");
console.dir(result);
if (result.btnId === "yes") {
// chat was accepted, change to ShowFullLog, join room and notify user
socket.send(
JSON.stringify({
command: "acceptchat",
payload: { email: chatRequestUser, roomId: chatRequestRoomId },
})
);
// collect necessary process log information
let response = await retryFetch(
`${config.API_BASE}/api/processes/${chatRequestProcessId}`
);
let process = await response.json();
let stateToPass = {
processId: chatRequestProcessId,
presName: process.prescriptionName,
presHistoryId: process.fiPrescriptionHistory._id,
autochat: true,
};
if (process.fiExpert?.email === auth.email) {
stateToPass.userEmail = process.fiInitiator?.email;
navigate("/fulllog", { state: stateToPass });
} else {
stateToPass.expertEmail = process.fiExpert?.email;
navigate("/userlog", { state: stateToPass });
}
} else {
// chat was refused, send message to requesting user
socket.send(
JSON.stringify({
command: "refusechat",
payload: {
email: chatRequestUser,
roomId: chatRequestRoomId,
},
})
);
}
};
useEffect(() => {
document.body.dir = currentLanguage?.dir || "ltr";
}, [currentLanguage]);
useEffectOnce(() => {
let messageUnsubscribe = distributionService
.getMessage()
.subscribe((msg) => {
console.log("Header incoming message");
switch (msg.command) {
case "requestchat":
setChatRequestUser(msg.payload.email);
setChatRequestProcessId(msg.payload.processId);
setChatRequestRoomId(msg.payload.roomId);
openMessageBox(
t("msg_chat_requested", {
user: msg.payload.email,
processId: msg.payload.processId,
}),
"requestchat",
[
{ id: "yes", text: t("yes") },
{ id: "no", text: t("no") },
],
[],
""
);
break;
}
});
return () => {
// cleanup subscription
messageUnsubscribe.unsubscribe();
};
}, []);
const login = () => {
navigate("/login");
};
const logout = async () => {
let response = await fetch(config.API_BASE + "/logout", {
credentials: "include",
});
if (!response.ok) alert(t("msg_error_logout"));
let result = await response.json();
console.log(result);
setAuth({});
socket.send(
JSON.stringify({
command: "logout",
payload: undefined,
})
);
navigate("/");
};
// const register = () => {
// navigate("/register");
// };
const showPrescriptions = () => {
//navigate("/design");
navigate("/prescriptionlist");
};
const goHome = () => {
navigate("/");
};
const openMenu = () => {
setShowLanguageDropdown(true);
};
const closeMenu = () => {
setShowLanguageDropdown(false);
};
const selectLanguage = (code) => {
i18next.changeLanguage(code);
setShowLanguageDropdown(false);
};
useOutsideClick(dropdownRef, closeMenu);
return (
<>
<div className="header-menu">
<div className="title" onClick={goHome}>
<img src={claryNextLogo} alt="ClaryNext logo" width="90" />
</div>
{!auth?.email ? (
<>
<div className="notonmobile">
<div>
<Anchor
onClick={showPrescriptions}
text={t("example_prescriptions")}
/>
</div>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<span> </span>
</div>
<button onClick={login}>{t("login")}</button>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
) : (
<>
<div className="rightMargin notonmobile">
<Anchor
onClick={showPrescriptions}
text={t("prescriptions_and_processes")}
/>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<Anchor onClick={logout} text={t("logout")} />
</div>
<span className="smaller">{auth.email}</span>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
)}
</div>
{showMessageBox ? (
<MessageBox
onButton={onButton}
buttons={msgBoxButtons}
text={msgBoxTitle}
inputs={msgBoxInputs}
id={msgBoxId}
moreJSX={msgBoxMoreJSX}
onClose={() => setShowMessageBox(false)}
/>
) : (
""
)}
{showLanguageDropdown ? (
<div className="language_dropdown" ref={dropdownRef}>
{availableLanguages.map((lang, idx) => (
<p key={idx} onClick={() => selectLanguage(lang.code)}>
<span
className={`flag-icon flag-icon-${lang.country_code}`}
></span>
{lang.name}
</p>
))}
</div>
) : (
""
)}
</>
);
}
export default Header;
Originally, I had the code to access the cookie in the main function scope. After I moved it into the useEffect[] callback, it started working also in Firefox.
I am trying to upload an image to my nodejs api/service using FileReader, the file is uploaded successfully but the page gets reloaded.
// html part
import React, { useState, useEffect, useCallback } from "react";
import { withRouter, useParams, useHistory } from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import Page from "./Page";
import TitleBar from "./TitleBar";
import SingleGalleryRow from "./SingleGalleryRow";
import CenteredPopup from "../common/CenteredPopup";
import { MdDelete,MdOutlineCrop,MdOutlineCircle } from "react-icons/md";
import Cropper from "react-easy-crop";
import getCroppedImg from "../shared/util/cropImage";
function EditImage{breadcrumb,pageName,setLoader,showToast}) {
const [list, setList] = useState([]);
const history = useHistory();
//const [files, setFiles] = useState([]);
const [umodalShow, setUModalShow] = useState(false);
const [row, setRow] = useState({
ImageName: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
Name: "",
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
const [isSubmitClicked, setIsSubmitClicked] = useState(false);
const [IsDisabled, setIsDisabled] = useState(false);
const [nameError, setNameError] = useState("");
const [validated, setValidated] = useState(0);
const { id } = useParams();
let content = "";
const [detail, setDetail] = useState({
Content: "",
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
Name: "",
SiteId: 1,
});
useEffect(() => {
let abortController = new AbortController();
//let isComponentMounted = true;
const fetchData = async () => {
// debugger;
try {
setLoader(true);
let apiUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/getbyId/" + id;
const response = await fetch(apiUrl, {
signal: abortController.signal,
});
const json = await response.json();
const resultPages = json ? json.list : "";
if (resultPages && resultPages.length > 0) {
//debugger;
let firstRow = resultPages[0];
let isEnabled = false;
if (firstRow.IsVisible === 1 || firstRow.IsVisible === true) {
isEnabled = true;
}
let newDetailPage = {
Id: firstRow.Id,
Name: firstRow.Name,
Content: firstRow.Content,
IsVisible: isEnabled,
IsDeleted: firstRow.IsDeleted,
SiteId: firstRow.SiteId,
CreatedBy: firstRow.CreatedBy,
CreatedOn: firstRow.CreatedOn,
ModifiedBy: firstRow.ModifiedBy,
ModifiedOn: firstRow.ModifiedOn,
};
setDetail(newDetailPage);
let itemRows = [];
for (let item of resultPages) {
let row = {
Id: item.DetailId,
ImageSliderId: item.ImageSliderId,
ImageName: item.ImageName,
ImageServerPath: item.ImageServerPath,
ImageFullUrl: item.ImageFullUrl,
ImageUrl: item.ImageUrl,
ImageType: item.ImageType,
ImageSize: item.ImageSize,
IsVisible: item.DetIsVisible,
IsDeleted: item.DetIsDeleted,
SiteId: item.DetSiteId,
CreatedBy: item.DetCreatedBy,
CreatedOn: item.DetCreatedOn,
ModifiedBy: item.DetModifiedBy,
ModifiedOn: item.DetModifiedOn,
};
if (item.ImageFullUrl) {
itemRows.push(row);
}
}
if (itemRows && itemRows.length > 0) {
setList(itemRows);
}
}
setLoader(false);
//abortController = null;
} catch (err) {
setLoader(false);
if (err.name === "AbortError") {
// Handling error thrown by aborting request
}
}
};
fetchData();
// Side-effect logic...
return () => {
// Side-effect cleanup
abortController.abort();
console.log("unmounting...");
};
}, []);
const [imageCropWidth, setImageCropWidth] = useState(100);
const [imageCropHeight, setImageCropHeight] = useState(100);
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [aspect, setAspect] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const [croppedImage, setCroppedImage] = useState(null);
const [cropSize, setCropSize] = useState({ width: 100, height: 100 });
const [Image, setImage] = useState("");
const [rotation, setRotation] = useState(0);
const [cropShape,setCropShape] = useState('rect');
const onSaveImage = async (e) => {
//debugger;
if (validate() < 0) {
e.preventDefault();
return -1;
}
try {
const croppedImage = await getCroppedImg(Image, croppedAreaPixels, rotation);
setCroppedImage(croppedImage);
setImage(croppedImage);
if (row) {
row.ImageFullUrl = croppedImage;
}
} catch (err) {
setLoader(false);
showToast("Error!", "Password couldn't be saved");
} finally {
setUModalShow(false);
}
};
const showImageForCrop = (e) => {
//debugger;
let localSrc = e.ImageFullUrl; //e.target.src;
setImage(localSrc);
setUModalShow(true);
setCropSize({width:(imageCropWidth ? parseInt(imageCropWidth):0),
height:(imageCropHeight ? parseInt(imageCropHeight):0)});
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id === e.Id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
};
const singlefileSelectedHandler = (e) => {
debugger;
e.preventDefault();
setIsDisabled(true);
content = "";
if (detail && detail.Content) {
content = detail.Content;
}
//let imageList = "";
const selectedImage = e.target.files[0];
let imageSizeInMBs = selectedImage.size ? selectedImage.size / 1024 / 1024 : 0;
if (selectedImage && imageSizeInMBs > 8) {
setIsDisabled(false);
setRow({
...row,
["ImageName"]: "",
["ImageServerPath"]: "",
["ImageFullUrl"]: "",
["ImageUrl"]: "",
});
showToast("Information!", "Image size can't be greater than 8MB.");
return -1;
}
//setFiles(...e.target.files);
const filename =
selectedImage && selectedImage.name
? new Date().valueOf() +
"_" +
selectedImage.name.replace(/\\/g, "").replace(/ /g, "").replace(/'/g, "").replace(/"/g, "").replace(/`/g, "")
: "";
debugger;
setUModalShow(false);
setCropSize({width:(imageCropWidth ? parseInt(imageCropWidth):0),
height:(imageCropHeight ? parseInt(imageCropHeight):0)});
const reader = new FileReader();
reader.onload = (ev) => {
//debugger;
ev.preventDefault();
let base64 = ev.target.result;
base64 = base64.replace(/^data:image\/(png|jpeg);base64,/, "");
setImage(reader.result);
//imgRef.current = e.target.result;
try {
//debugger;
const settings = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
image: base64,
filename: filename,
}),
//signal: abortController.signal,
};
//setUModalShow(true);
fetch(process.env.REACT_APP_SERVER_DOMAIN + `/uploaddetail`, settings)
.then((response) => response.json())
.then((response) => {
debugger;
//const uploadedImageUrl = response.data.url; // get saved image url
const uploadedImageUrl = response.fullurl; // get saved image url
const uploadedImageName = response.imagename;
const uploadedServerPath = response.serverpath;
const uploadurl = response.url;
setRow({
...row,
["ImageName"]: uploadedImageName,
["ImageServerPath"]: uploadedServerPath,
["ImageFullUrl"]: uploadedImageUrl,
["ImageUrl"]: uploadurl,
});
if (uploadedImageUrl) {
setIsDisabled(false);
}
content +=
"<div class='slide'><div class='slider-image embed-responsive-16by9' style='background-image: url(" +
'http://localhost:3001/' + uploadedImageUrl +
");'></div></div>";
setDetail({ ...detail, ["Content"]: content });
})
.catch((e) => {
debugger;
console.log(e);
setIsDisabled(false);
});
} catch (error) {
debugger;
setIsDisabled(false);
}
};
reader.readAsDataURL(selectedImage);
};
const validate = () => {
//debugger;
if (detail && !detail.Name) {
setNameError("Name is mandatory.");
return -1;
}
setNameError("");
return 0;
};
const HandleSaveMenu = async (e) => {
//debugger;
e.preventDefault();
const form = e.currentTarget;
if (validate() < 0) {
// e.preventDefault();
return -1;
}
if (form.checkValidity() === false) {
e.stopPropagation();
const formData = new FormData(form);
const validationMessages = Array.from(formData.keys()).reduce((acc, key) => {
acc[key] = form.elements[key].validationMessage;
return acc;
}, {});
alert(validationMessages);
//console.log(validationMessages)
} else if (form.checkValidity() === true) {
//setIsSubmitClicked(true);
//debugger;
content = `<div class="grid"><div class="grid-sizer"></div>`;
if (list && list.length > 0) {
//debugger;
let listcounter = 0;
for (let item of list) {
content += `<div class="grid-item"><img src="${item.ImageFullUrl}" alt="${item.ImageName}" /></div>`;
}
content += `</div>`;
setDetail({ ...detail, ["Content"]: content });
}
//debugger;
const settings = {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: detail.Name,
content: content /*detail.Content*/,
imageDetail: list,
siteId: 1,
isVisible: detail.IsVisible === true ? 1 : 0,
isDeleted: false,
CreatedBy: 1,
ModifiedBy: 1,
}),
};
try {
setLoader(true);
let submitUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/" + id;
const fetchResponse = await fetch(submitUrl, settings);
if (!fetchResponse.ok) {
// => false
//const statusCode = fetchResponse.status; // => 404
const text = await fetchResponse.text();
//console.log(text);
}
const retdata = await fetchResponse.json();
let resultSet = retdata ? retdata.list : "";
setLoader(false);
if (resultSet && resultSet.length > 0 && resultSet[0].affectedRows > 0) {
history.push("/backend/imagegallerylist");
showToast("Information!", "Gallery has been saved successfully.");
}
//return resultSet;
} catch (e) {
setLoader(false);
}
}
setValidated(1);
};
const onAddSide = (e) => {
debugger;
let newImageList = [...list];
if (row.Id && row.Id > 0) {
if (newImageList && newImageList.length > 0) {
const selectedIndex = newImageList.findIndex((item) => item.Id === row.Id);
if (selectedIndex > -1) {
newImageList[selectedIndex] = row;
}
}
} else {
row.Id = Math.max.apply(
Math,
newImageList.map(function (o) {
return o.Id + 1;
})
);
if (!row.Id || (row.Id && row.Id === 0) || row.Id === -Infinity) {
row.Id = 1;
}
newImageList.push(row);
}
setList(newImageList);
setImage("");
setRow({
ImageName: "",
Name: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
};
const onDeleteSlide = (e) => {
//debugger;
const dataToShow = list ? list.filter((item) => item.Id.toString() !== e.target.id) : list;
setList(dataToShow);
};
const onEditSlide = (e) => {
// debugger;
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id.toString() === e.target.id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
};
const onCropperZoomChange =(e) => {
if(e && e.currentTarget && e.currentTarget.value)
{
let lZoomValue = e.currentTarget.value;
if(lZoomValue && lZoomValue < 0.4)
{
//setZoom(0.28);
setZoom(0.4);
}
else if(lZoomValue && lZoomValue > 1)
{
setZoom(1);
}
else{
setZoom(lZoomValue);
}
}
};
return (
<Page breadcrumb={breadcrumb} pageName={pageName} fluid>
<form noValidate validated={validated} onSubmit={HandleSaveMenu} className="user-form">
<Card>
<Card.Header>
<TitleBar title="Edit Gallery" />
</Card.Header>
<Card.Body>
<div className="form-row">
<div className="form-group col-md-4">
<label className="form-label" htmlFor="Name">
Name
</label>
<input
name="Name"
required
id="Name"
className={`form-control ${nameError ? "is-invalid" : ""}`}
value={detail.Name}
onChange={handleFormChanges}
/>
{nameError ? <div className="invalid-feedback">{nameError}</div> : ""}
{!nameError && <div className="valid-feedback">Looks good!</div>}
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Width
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropWidth}
onChange={(e) => setImageCropWidth(e.target.value)}
/>
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Height
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropHeight}
onChange={(e) => setImageCropHeight(e.target.value)}
/>
</div>
<div className="form-group col-md-4">
<label className="form-label"> </label>
<div className="custom-control custom-switch">
<input
id="IsVisible"
name="IsVisible"
checked={detail.IsVisible}
onChange={handleFormChanges}
type="checkbox"
className="custom-control-input"
/>
<label title="" htmlFor="IsVisible" className="custom-control-label">
Active
</label>
</div>
</div>
</div>
<SingleGalleryRow
data={row}
setRow={setRow}
onUpload={singlefileSelectedHandler}
onAddSide={onAddSide}
IsDisabled={IsDisabled}
/>
<ul className="slide-list">
{list &&
list.map(function (item, i) {
//debugger;
return (
<li key={i}>
<div className="slide-row">
<div className="img-holder">
<img
className="thumbnail"
src={item.ImageFullUrl}
alt={item.ImageName}
onClick={() => showImageForCrop(item)}
/>
</div>
<div className="btns-holder">
<button
type="button"
id={item.Id}
onClick={onDeleteSlide}
className="btn btn-circle btn-sm btn-danger"
>
<MdDelete />
</button>
</div>
</div>
</li>
);
})}
</ul>
</Card.Body>
<Card.Footer>
<div className="btn-holder text-right">
<Button type="submit" variant="primary" disabled={isSubmitClicked}>
Save
</Button>
</div>
</Card.Footer>
</Card>
</form>
<CenteredPopup
show={umodalShow}
onHide={() => setUModalShow(false)}
title="Crop Image"
content=""
closetext="Close"
savetext="Apply"
onSaveChanges={onSaveImage}
>
<div className="cropimage-container">
<div className="cropimage-tools">
<span className="cropimage-icons"
onClick={(e) => setCropShape('rect')}
>
<MdOutlineCrop />
<span className="cropimage-text">
Crop
</span>
</span>
<span className="cropimage-icons"
onClick={(e) => setCropShape('round')} >
<MdOutlineCircle />
<span className="cropimage-text">
Circle
</span>
</span>
</div>
<div className="crop-container">
<Cropper
image={Image}
crop={crop}
cropSize={cropSize}
cropShape={cropShape}
zoom={zoom || 1}
showGrid={true}
zoomWithScroll={false}
objectFit="horizontal-cover"
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={onCropperZoomChange}
/>
</div>
</div>
<div className="form-row ic_rangeslider">
<div className="form-group col-md-12">
<label className="form-label" htmlFor="ZoomBtn">
{zoom || 1}
</label>
<span className="range-slider__wrap">
<input
id="zoomrangeid"
type="range"
className="slider range-slider range-slider--light"
value={zoom || 1}
min={0.4}
max={1}
step={0.1}
aria-labelledby="Zoom"
onChange={onCropperZoomChange}
/>
</span>
</div>
</div>
</CenteredPopup>
</Page>
);
}
export default withRouter(EditImage);
I am trying to fix it from four hours by reading through the articles from google.
I don't want the page to be reloaded/refreshed. My image files are being uploaded successfully and my node js service is returning the result in json.
If you need anything, please ask.
Why my browser is being reloaded/refreshed when my fileUpload is complete?
My client package.json
{
"name": "test1",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.6.0",
"faker": "^5.5.3",
"node-sass": "^4.14.1",
"react": "^17.0.2",
"react-bootstrap": "^1.6.1",
"react-dom": "^17.0.2",
"react-easy-crop": "^4.0.0",
"react-icons": "^4.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"styled-components": "^5.3.0",
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
},
}
I am trying to upload a profilePhoto from reactjs to express backend using FormData:
const form = new FormData();
form.append(user, "user");
form.append(profilePhoto, "profilePhoto");
axios({
method: "post",
url: "http://localhost:8082/candidate/addCandidate",
data: form,
headers: { "Content-Type": "multipart/form-data" },
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
in the backend : (this works well with the postman and the image gets added to the backend)
const DIR = "./public/";
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = file.originalname.toLowerCase().split(" ").join("-");
cb(null, uuid() + "-" + fileName);
},
});
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (
file.mimetype === "image/png" ||
file.mimetype === "image/jpg" ||
file.mimetype === "image/jpeg"
) {
cb(null, true);
} else {
cb(null, false);
return cb(new Error("Only .png, .jpg and .jpeg format allowed!"));
}
},
});
router.post(
"/addCandidate",
upload.single("profilePhoto"),
(req, res, next) => {
const url = req.protocol + "://" + req.get("host");
// try {
const candidate = new Candidate({
user: req.body.user,
profilePhoto: url + "/public/" + req.file,
});
candidate
.save()
.then((result) => {
console.log("bbbbbbbbbbbb", req);
res.status(201).json({
message: "User registered successfully!",
profilePhoto: result.profilePhoto,
});
})
.catch((err) => {
console.log("aaaaaaaaaaaa", req.body.file);
res.status(500).json({ error: err });
console.log(err);
});
}
);
however, when ever i try to upload a file it doesnt get uploaded to the backend and i get req.file undefined. I tried to console the req and this is what i get body:
{'606aee02f057cd714db2b646': 'user', '[object File]': 'profilePhoto'}
Any help!
I use something like this, you can change it for your project
My component:
import React from "react";
import ConfigX from '../ConfigX'
class FileUploader extends React.Component
{
url_upload = "";
url_download = "";
apiKey = "";
typ = "news";
typ_id = 99999;
constructor(props)
{
super(props)
this.state = {
view: "list",
title: "wybierz plik",
files: [],
uploadProcentAll: 10,
}
this.url_upload = ConfigX.restApi + "/rest,file_upload";
this.url_download = ConfigX.restApi + "/rest,file_list";
this.url_delete = ConfigX.restApi + "/rest,file_delete";
this.apiKey = this.props.apiKey;
this.typ = this.props.typ;
this.typ_id = this.props.typ_id;
}
/**
* Get actual list of file uploaded to server
*/
componentDidMount()
{
var dataPost = {
};
fetch( this.url_download , {
method: 'POST',
body: JSON.stringify(dataPost),
headers: {
'Content-Type': 'text/html',
'X-API-KEY': this.apiKey
}
})
.then( res => res.json() )
.then(json => {
// this.setState({
// files: json
// });
});
}
onChangeInput(event)
{
var handle = event.target;
console.log("FileUploader, selected: " + handle.files.length );
if( handle.files.length == 0) return false;
for(var i = 0; i < handle.files.length; i++)
{
var file = handle.files[i];
var isAdded = false;
this.state.files.forEach( exFile => {
if(exFile.name == file['name']) isAdded = true;
});
if(isAdded) continue;
var randName = crypto.getRandomValues( new Uint32Array(1)) + file['name'];
var item = {
name: file['name'],
progress: 0,
status: 'start',
uq: randName
}
var list = this.state.files;
list.push(item);
this.setState({ files: list});
this.startUpload(file, list.length-1, randName );
}
}
onDelete(event)
{
var uq = event.target.getAttribute("uq");
console.log("FileUploader, delete: " + uq);
var dataPost = {
uq: uq
};
fetch( this.url_delete , {
method: 'POST',
body: JSON.stringify(dataPost),
headers: {
'Content-Type': 'text/html',
'X-API-KEY': this.apiKey
}
})
.then( res => res.json() )
.then(json => {
if(json.status == "OK") //deleted on server..
{
var files = this.state.files.filter( item => {
if(item.uq == uq) return false;
return true;
} );
this.setState({
files: files
})
}
});
}
refreshProgress()
{
var sumP = 0;
var countP = 0;
this.state.files.map( item => {
if(item.status == 'start' || item.status == 'upload')
{
countP++;
sumP += item.progress;
}
} );
var avg = sumP / countP;
this.setState({
uploadProcentAll: avg
});
}
startUpload(file, tabIndex, randName)
{
var refState = this.state;
var refRefreshProgress = this.refreshProgress.bind(this);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress',function(ev)
{
var procent = (ev.loaded/ev.total );
procent *= 100;
procent = Math.round(procent) ;
refState.files[tabIndex]['progress'] = procent;
refState.files[tabIndex]['status'] = "upload";
refRefreshProgress();
}, false)
xhr.addEventListener("load", function(ev){
} , false);
xhr.addEventListener("error", function(ev){}, false);
xhr.addEventListener("abort", function(ev){}, false);
xhr.onreadystatechange = function(ev)
{
if (xhr.readyState == 4)
{
refState.files[tabIndex]['status'] = "finish";
refState.files[tabIndex]['progress'] = 100;
}
};
xhr.open('POST', this.url_upload, true);
xhr.setRequestHeader('X-API-KEY', this.apiKey);
var data = new FormData();
data.append('file'+tabIndex, file );
data.append('typ', this.typ);
data.append('typ_id', this.typ_id);
data.append('fileUploader','yes');
data.append('uq', randName);
xhr.send(data);
}
render()
{
var progress = "";
var progressStyle = {
width: this.state.uploadProcentAll+"%"
}
if(this.state.uploadProcentAll > 0 && this.state.uploadProcentAll < 100 )
{
progress = (
<div className="progressBar">
<div className="progressLine" style={progressStyle}>{this.state.uploadProcentAll}</div>
</div>
)
}
return (
<div className="fileUploader">
<div className="buttonUploader">
<input type="file" onchange="zaladuj_pliki(this)" multiple id="fileselect" name="fileselect[]" accept="*/*" onChange={this.onChangeInput.bind(this)} />
{progress}
</div>
<table>
{
this.state.files.map( (item,index) => {
return (
<tr key={index} >
<td>{item.progress}</td>
<td>{item.name}</td>
<td uq={item.uq} onClick={this.onDelete.bind(this) } >Del</td>
</tr>
)
})
}
</table>
</div>
)
}
}
export default FileUploader;
Put in parent:
<FileUploader typ="sk_szkolenia" typ_id={this.state.rows.id} apiKey={this.props.apiKey} />