raintrack-ui/src/components/FileUploadForm.js

129 lines
4.5 KiB
JavaScript

import React, {useCallback, useContext, useEffect, useReducer, useRef, useState} from 'react';
import {toast} from "react-toastify";
import {AuthContext} from "../App";
import {SERVER_URL} from "../auth";
import {Input} from "../custom";
const LIMIT = 2 * 1024 * 1024; // 2 mb
const dispatch = (state, action) => {
switch (action.type) {
case "lat":
return {...state, lat: action.value}
case "long":
return {...state, long: action.value}
default:
return;
}
}
function FileUploadForm() {
const {authState} = useContext(AuthContext)
const [files, setFiles] = useState(null);
const [load, setLoad] = useState(0)
const [status, setStatus] = useState("Upload a file")
const [latLong, setLatLong] = useReducer(dispatch, {lat: "", long: ""}, undefined)
let form = useRef(null);
async function query(url, data) {
console.debug("FETCH: " + SERVER_URL + url + " - " + data)
return new Promise((resolve, reject) => {
let req = new XMLHttpRequest()
req.open('post', SERVER_URL + url)
req.upload.addEventListener('progress', e => {
if (e.loaded === e.total) setStatus("Processing...")
else {
let percent = (e.loaded / e.total * 100)
setLoad(percent)
setStatus("Uploading " + percent.toFixed(0) + "%")
}
});
req.addEventListener('load', () => {
resolve({status: req.status, json: JSON.parse(req.response)})
});
req.addEventListener('error', e => {
resolve({status: req.status, json: JSON.parse(req.response)})
})
req.send(data)
})
}
const upload = useCallback(async () => {
if (!files || files.length === 0) return
const target = files[0]
if (target.size > LIMIT) return toast.error("File size too large! Max 2 mb.")
if (!target.name.includes(".xlsx")) return toast.error("Invalid file type!")
const {lat, long} = latLong;
setStatus("Uploading")
let file = new FormData()
file.append("file", target)
file.append("token", authState.token);
file.append("latitude", lat);
file.append("longitude", long);
let {status, json} = await query("/data/upload", file)
form.current.reset();
setStatus("Upload a file")
setLoad(0)
if (status !== 200) return toast.error(json.reason)
return toast.success("File successfully uploaded.")
}, [files, authState.token, form, latLong])
useEffect(() => {
(async () => upload())();
}, [files, authState.token, upload, authState, setLoad, setStatus])
return <div className="bg-white shadow p-4 overflow-hidden rounded-lg flex flex-col justify-center
my-6 px-6 w-11/12 lg:mx-2 lg:my-5 lg:px-5 lg:w-5/12">
<div>
<div className="text-lg font-medium text-center mb-2">Upload File Entries</div>
<div className="text-xs font-light text-center text-black mb-2">
Click the button to upload an .xlsx file
</div>
<hr className="mb-6 border-gray-300 mx-12"/>
</div>
<form className="mt-2 items-center" ref={form}>
<p className="text-gray-500 italic text-xs ">Latitude and longitude is required if your data doesn't have it</p>
<Input name="lat" type="text" placeholder="Latitude"
setValue={e => setLatLong({type: "lat", value: e})}/>
<Input name="long" type="text" placeholder="Longitude"
setValue={e => setLatLong({type: "long", value: e})}/>
<div className="w-full flex justify-center">
<label className="px-4 pb-2 bg-white text-blue rounded-lg shadow-lg loading inline-block
relative border border-blue cursor-pointer bg-blue-500 hover:bg-blue-700 text-white">
<style dangerouslySetInnerHTML={{
__html:
'.loading::after {' +
'content: "";' +
`width: ${load}%;` +
'background: rgb(255, 255, 255, 0.4);' +
'position: absolute;' +
'top: 0;' +
'bottom: 0;' +
'left: 0;' +
'transition: width 0.3s;'
}}/>
<div className="flex items-center">
<svg className="w-4 h-4 mt-2 mr-3" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path
d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z"/>
</svg>
<span className="mt-2 font-bold">{status}</span>
<input type='file' className="hidden" name="upload" accept=".xlsx"
onChange={e => setFiles(e.target.files)}/>
</div>
</label>
</div>
</form>
</div>;
}
export default FileUploadForm;