129 lines
4.5 KiB
JavaScript
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; |