import React from 'react' import { connect, ConnectedProps } from 'react-redux' import stringify from 'csv-stringify' import { t, c } from 'ttag' import parse from 'csv-parse' import { UserSite } from '../reducers/runConfiguration' import { addUserSite, addUserSites, removeUserSite, setUserSiteLabel, setActiveUserSite } from '../actions/point' import { setMapMode } from '../actions/map' import ModalCard from '../components/ModalCard' import { variables } from '../config' type State = { runConfiguration: { objective: 'seedlots' | 'sites' userSites: UserSite[] } map: { mode: string } } const connector = connect( ({ runConfiguration: { objective, userSites }, map: { mode } }: State) => ({ objective, userSites, mode, }), dispatch => ({ removeSite: (index: number) => dispatch(removeUserSite(index)), onAddUserSite: (lat: number, lon: number, label: string) => { dispatch(addUserSite({ lat, lon }, label)) dispatch(setMapMode('normal')) }, onAddUserSites: (sites: { latlon: { lat: number; lon: number }; label: string }[]) => dispatch(addUserSites(sites)), onSetUserSiteLabel: (label: string, index: number) => dispatch(setUserSiteLabel(label, index)), onMouseOverSite: (index: number | null) => dispatch(setActiveUserSite(index)), onSetMapMode: (mode: string) => dispatch(setMapMode(mode)), }), ) type ComparisonsProps = ConnectedProps const Comparisons = ({ objective, userSites, mode, removeSite, onAddUserSite, onAddUserSites, onSetUserSiteLabel, onMouseOverSite, onSetMapMode, }: ComparisonsProps) => { const [expandLevel, setExpandLevel] = React.useState(0) const expandClasses = ['', 'preview', 'full-height'] const expandMessages = [t`Click to show`, t`Click for full height`, t`Click to hide`] const [newSite, setNewSite] = React.useState({ lat: '', lon: '', label: '' }) const [activeEdit, setActiveEdit] = React.useState(null as { lat: number; lon: number; label: string } | null) const [processingCSV, setProcessingCSV] = React.useState(false) const [csvError, setCSVError] = React.useState(null as string | null) const fileInputRef = React.useRef(null) let siteVariables: string[] = [] if (userSites.length) { siteVariables = Object.keys(userSites[0].deltas || {}) } return (
{ if (files?.length) { setProcessingCSV(true) const file = files[0] const reader = new FileReader() reader.onload = e => { if (e.target?.result) { parse(e.target.result as string, { columns: true }, (err, rows: [{ [key: string]: string }]) => { if (err) { setCSVError(err.message) setProcessingCSV(false) } else { if (!rows.length) { setCSVError(t`The file is empty.`) setProcessingCSV(false) return } const columns = Object.keys(rows[0]) const xCol = columns.find(name => ['x', 'lon', 'long', 'longitude', 'longitud'].includes(name.toLowerCase().trim()), ) const yCol = columns.find(name => ['y', 'lat', 'latitude', 'latitud'].includes(name.toLowerCase().trim()), ) const labelCol = columns.find(name => ['name', 'label'].includes(name.toLowerCase().trim())) if (!(xCol && yCol)) { setCSVError(t`The CSV has no latitude and/or longitude column.`) setProcessingCSV(false) return } const sites = rows.map(row => { const site = { latlon: { lat: Number.parseFloat(row[yCol]), lon: Number.parseFloat(row[xCol]), }, } as { latlon: { lat: number; lon: number }; label: string } if (labelCol) { site.label = row[labelCol] } return site }) onAddUserSites(sites) setProcessingCSV(false) } }) } } reader.readAsText(file) } }} />
{userSites.length ? ( {siteVariables.map(variable => ( ))} ) : null} {userSites.map((site, index) => { const editRow = activeEdit && activeEdit.lat === site.lat && activeEdit.lon === site.lon return ( onMouseOverSite(index)} onBlur={() => onMouseOverSite(null)} onMouseOver={() => onMouseOverSite(index)} onMouseOut={() => onMouseOverSite(null)} > {siteVariables.map(variable => { if (site.deltas) { let value = site.deltas[variable] const variableConfig = variables.find(v => v.name === variable) if (variableConfig) { value /= variableConfig.multiplier } return } return })} ) })}
{t`Location`} {t`Name`} {t`Match`}Δ {variable}
{`${site.lat.toFixed(2)}, ${site.lon.toFixed(2)}`} {editRow && (
{ onSetUserSiteLabel(activeEdit!.label, index) setActiveEdit(null) return false }} onKeyDown={e => { if (e.key === 'Escape') { setActiveEdit(null) } }} > {/* eslint-disable jsx-a11y/no-autofocus */} setActiveEdit({ ...activeEdit!, label: e.target.value })} onBlur={() => { onSetUserSiteLabel(activeEdit!.label, index) setActiveEdit(null) }} /> {/* eslint-enable */}
)} {!editRow && (site.label ? ( ) : ( ))}
{site.score !== undefined ? `${site.score}%` : c('Not Applicable').t`N/A`}{Number(value.toFixed(1))}{c('Not Applicable').t`N/A`}
{mode === 'add_sites' ? (
{ e.preventDefault() const { lat, lon, label } = newSite const latF = parseFloat(lat) const lonF = parseFloat(lon) if (latF && lonF) { onSetMapMode('normal') onAddUserSite(latF, lonF, label) } return false }} onKeyDown={e => { if (e.key === 'Escape') { onSetMapMode('normal') setNewSite({ lat: '', lon: '', label: '' }) } }} >
) : ( <> {userSites.length ? ( ) : null} )}
{(processingCSV || csvError) && ( <>
) } > {csvError ? (
{csvError}
) : ( <>
{t`Uploading CSV data...`}
)} )} ) } export default connector(Comparisons)