<|start_filename|>src/components/Modal/Contents/LayerFilter.js<|end_filename|>
import React from 'react'
import DataTable from '../../DataTable/DataTable.js'

const LayerFilter = ({isFilterEmptyResult, isLoadingFilter, layers, modalLayerFilterName, onLayerFilterSearch}) => {
    const handleFilter = e => {
        e.preventDefault()
        onLayerFilterSearch(modalLayerFilterName, e.target.elements.parameterKey.value, e.target.elements.parameterValue.value.toLocaleLowerCase())
    }

    const filteredLayers = layers.filter(l => l.name === modalLayerFilterName)
    if (filteredLayers.length === 0) {
        return <p>Erro ao carregar parâmetros.</p>
    }
    const filteredLayer = filteredLayers[0]

    return (
        <div>
            <p>Selecione um parâmetro, digite um valor e clique em Filtrar:</p>
            <form onSubmit={handleFilter}>
                <select name="parameterKey">
                    {filteredLayer.params.map((param, idx) => <option key={idx}>{param}</option>)}
                </select>
                <input type="text" name="parameterValue"/>
                <button type="submit">Filtrar</button>
            </form>
            {isLoadingFilter ? <p>Aguarde, carregando dados...</p> : null}
            {isFilterEmptyResult ? <p>Não foram encontrados dados para o valor buscado.</p> : null}
            {filteredLayer.filteredData ?
                <DataTable layer={filteredLayer} isLayerFilter={true}/>
            : null}
        </div>
    )
}

export default LayerFilter


<|start_filename|>src/components/Api/GeoAPI.js<|end_filename|>
import axios from 'axios'

const WORKSPACE = __WORKSPACE__
const ENDPOINT  = __API__

const GeoAPI = {

    workspace: WORKSPACE,
    restricted: false,
    layers: [],
    menu: [],

    createUrl({layer, clickData, featureCount}) {
        if (layer.styles && layer.selectedLayerStyleId) {
            var styleName = layer.styles[layer.selectedLayerStyleId].name.replace('plataforma:', '')
        } else {
            var styleName = ""
        }
        let CQLFilter = '1%3D1%3B1%3D1'
        if (layer.filterKey && layer.filterValue) {
            CQLFilter = `strToLowerCase(${layer.filterKey}) LIKE '%25${layer.filterValue}%25'`
        }
        return `?LAYERS=${layer.name}&QUERY_LAYERS=${layer.name}&STYLES=${styleName},&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&BBOX=${clickData.BBOX}&FEATURE_COUNT=${featureCount}&HEIGHT=${clickData.HEIGHT}&WIDTH=${clickData.WIDTH}&FORMAT=image%2Fpng&INFO_FORMAT=application%2Fjson&SRS=EPSG%3A4326&X=${clickData.X}&Y=${clickData.Y}&CQL_FILTER=${CQLFilter}`
    },

    /**
    * Call GeoServer and get XML data for places
    * @param callback function to call when data is fully loaded
    */
    getContent(callback) {
        axios
            .get(ENDPOINT + '?request=GetCapabilities')
            .then(response => {
                callback({
                    xmlData: response
                })
            })
            .catch(error => {
                return console.log(error)
            })
    },

    /**
    * Call GeoServer and get layer feature data
    * @param callback function to call when data is fully loaded
    */
    getLayerData(callback, url) {
        axios
            .get(ENDPOINT + url)
            .then((response) => {
                callback(response.data)
            })
            .catch((error) => {
                return console.log(error)
            })
    },

    /**
    * Call GeoServer and get layer feature data
    * @param callback function to call when data is fully loaded
    */
    getLayersData(callback, urls) {
        axios.all(urls.map(l => axios.get(ENDPOINT+l)))
        .then(axios.spread(function (...res) {
            // all requests are now complete
            let responses = res.map( r => r.data)
            callback(responses)
        }))
        .catch((error) => {
            return console.log(error)
        })
    },

    /**
    * Call GeoServer and get polygon data
    * @param callback function to call when data is fully loaded
    */
    getPolygonData(callback, coordinates, activeLayers) {
        const PREFIX = 'plataforma:'
        const layers = [
            {
                'name': 'Escolas',
                'url': 'educ_escolas_busca_pol_4326',
            },
            {
                'name': 'População',
                'url': 'se_setores_2010_busca_pol_4326',
            },
            {
                'name': 'Bombeiros',
                'url': 'bombeiro_busca_pol_4326',
            },
            {
                'name': 'Guarda Municipal',
                'url': 'guarda_municipal_busca_pol_4326',
            },
            {
                'name': 'B<NAME>',
                'url': 'policia_batalhao_sede_busca_pol_4326',
            },
            {
                'name': '<NAME>',
                'url': 'policia_dps_busca_pol_4326',
            },
            {
                'name': 'Hospitais',
                'url': 'saude_estabelecimentos_cnes_busca_pol_4326',
            },
            {
                'name': 'Estações de BRT',
                'url': 'trans_brt_estacoes_busca_pol_4326',
            },
            {
                'name': 'Estações de Metrô',
                'url': 'trans_metro_estacoes_busca_pol_4326',
            },
            {
                'name': 'Estações de Ônibus',
                'url': 'trans_onibus_por_embarque_busca_pol_4326',
            },
            {
                'name': 'Estações de Trem',
                'url': 'trans_trem_estacoes_busca_pol_4326',
            },
            {
                'name': 'Estações de VLT',
                'url': 'trans_vlt_estacoes_busca_pol_4326',
            },
        ]
        coordinates = coordinates[0]
        coordinates = coordinates.map((c) => {
            return c.lng + ' ' + c.lat
        })
        coordinates.push(coordinates[0])

        coordinates = coordinates.join(',')

        const urls = layers.map( l =>
            ENDPOINT+`?service=WFS&version=1.0.0&request=GetFeature&typeName=${PREFIX+l.url}&outputFormat=application%2Fjson&cql_filter=INTERSECTS(geom,  POLYGON((${coordinates})))`
        )
        axios.all(urls.map(l => axios.get(l)))
        .then(axios.spread(function (...res) {
            // all requests are now complete
            let responses = res.map( r => {
                let copy = r.data.features.slice()
                let subs
                let category
                if (copy.length > 0){
                    subs = copy[0].id.split('.')[0]
                    let result = layers.filter(l => l.url === subs)
                    category = result[0].name
                }
                return r.data.features.map( f => {
                    return {
                        category,
                        "geometry": f.geometry,
                        "id": f.id,
                        "properties": f.properties,
                    }
                })
            })
            callback(responses)
        }))
        .catch((error) => {
            return console.log(error)
        })
    },

    /**
     *
     * @param {Object} layer The layer to get params from
     * @param {String} layer.layerName The layerName param on layer, to write URL. Typically it is 'plataforma:xxxx'
     * @param {Function} callback function to call when data is fully loaded
     */
    getLayerParams(layer, callback) {
        axios
            .get(ENDPOINT.replace('wms', 'wfs') + `?request=describeFeatureType&typename=${layer.layerName}`)
            .then(response => {
                callback(response.data)
            })
            .catch(error => {
                console.log(error)
            })
    },

    getLayerFilteredData(layerName, parameterKey, parameterValue, callback) {
        axios
            .get(ENDPOINT.replace('wms', 'wfs') + `?service=wfs&version=2.0.0&request=GetFeature&typename=plataforma:${layerName}&outputformat=application/json&CQL_FILTER=strToLowerCase(${parameterKey})%20LIKE%20%27%25${parameterValue}%25%27&count=99999&sortBy=${parameterKey}`)
            .then(response => {
                callback(response.data)
            })
            .catch(error => {
                console.log(error)
            })
    },
}

export default GeoAPI


<|start_filename|>src/components/LayerStylesCarousel/LayerStylesCarousel.js<|end_filename|>
import React from 'react'
import LayerStyleItem from './LayerStyleItem'

const LayerStylesCarousel = ({ layer, onArrowLeftClick, onArrowRightClick, onStyleClick }) => {

    const STYLE_WIDTH = 66

    let selectedLayerId

    let leftArrowClassName  = 'layer-styles-carousel--arrow fa fa-chevron-left'
    let rightArrowClassName = 'layer-styles-carousel--arrow fa fa-chevron-right'

    let layerStylesLength = 0
    let layerStylesPositionCounter = 0

    if (layer) {
        if (layer.styles && layer.styles.length < 6) {
            leftArrowClassName  += ' remove'
            rightArrowClassName += ' remove'
        } else {
            if (!layer.stylesPositionCounter) {
                leftArrowClassName += ' hidden'
            } else if (layer.stylesPositionCounter === layer.styles.length - 5) {
                rightArrowClassName += ' hidden'
            }
        }

        if (layer.styles) {
            layerStylesLength = layer.styles.length * STYLE_WIDTH
        }
        if (layer.stylesPositionCounter) {
            layerStylesPositionCounter = layer.stylesPositionCounter
        }
    }

    let carouselListStyle = {
        width: layerStylesLength + 'px',
        marginLeft: -(layerStylesPositionCounter * STYLE_WIDTH) + 'px',
    }

    function arrowLeftClick() {
        return onArrowLeftClick(layer)
    }

    function arrowRightClick() {
        return onArrowRightClick(layer)
    }

    if (layer.stylesOrdered) {
        layer.styles.sort((a, b) => {
            if (a.title > b.title) {
                return 1
            }
            if (a.title < b.title) {
                return -1
            }
            return 0
        }).reverse()
    }

    return (
        <div className="layer-styles-carousel">
            <a role="button" className={leftArrowClassName} onClick={arrowLeftClick}></a>
            <div className="layer-styles-carousel--list-container">
                <ul className="layer-styles-carousel--list" style={carouselListStyle}>
                    {
                        layer && layer.styles ?
                            layer.styles.map((style, indexStyle) => {
                                return (
                                    <LayerStyleItem
                                        layer={layer}
                                        style={style}
                                        key={indexStyle}
                                        index={indexStyle}
                                        onStyleClick={onStyleClick}
                                    />
                                )
                            })
                        : ''
                    }
                </ul>
            </div>
            <a role="button" className={rightArrowClassName} onClick={arrowRightClick}></a>
        </div>
    )
}

export default LayerStylesCarousel


<|start_filename|>src/components/Charts/Charts.js<|end_filename|>
import React from 'react'
import {Bar, Line, HorizontalBar, Pie} from 'react-chartjs-2'

const Charts = ({ layer }) => {
    // standard colors
    let colorsArray = [
        // red, green, blue
        {
            backgroundColor: 'rgba(255,0,0,.2)',
            borderColor: 'rgba(255,0,0,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(255,0,0,.4)',
            hoverBorderColor: 'rgba(255,0,0,0)',
        },
        {
            backgroundColor: 'rgba(0,255,0,.2)',
            borderColor: 'rgba(0,255,0,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(0,255,0,.4)',
            hoverBorderColor: 'rgba(0,255,0,0)',
        },
        {
            backgroundColor: 'rgba(0,0,255,.2)',
            borderColor: 'rgba(0,0,255,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(0,0,255,.4)',
            hoverBorderColor: 'rgba(0,0,255,0)',
        },
        {
            backgroundColor: 'rgba(1,122,122,.2)',
            borderColor: 'rgba(1,122,122,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(1,122,122,.4)',
            hoverBorderColor: 'rgba(1,122,122,0)',
        },
        {
            backgroundColor: 'rgba(122,1,122,.2)',
            borderColor: 'rgba(122,1,122,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(122,1,122,.4)',
            hoverBorderColor: 'rgba(122,1,122,0)',
        },
        {
            backgroundColor: 'rgba(122,122,1,.2)',
            borderColor: 'rgba(122,122,1,0)',
            borderWidth: 1,
            hoverBackgroundColor: 'rgba(122,122,1,.4)',
            hoverBorderColor: 'rgba(122,122,1,0)',
        },
    ]

    // treat invalid values
    const parseValue = (value) => {
        if (!value) {
            return 0
        }
        if (value === 'não informado') {
            return 0
        }
        return value
    }

    const dataObject = (chart, features) => {
        let dataObj = {
            labels: [],
            datasets: [],
        }

        // get chart labels
        for (var c = 0, lc = chart.columns.length; c < lc; c++) {
            dataObj.labels.push(chart.columns[c][0])
        }

        // if we have valid columns
        if (features) {
            for (let f = 0, lf = features.length; f<lf; f++) {
                // for each element on the chart
                let dataset = {
                    // get the element entity name
                    label: features[f].properties[chart.entity] || '',
                    data: [],
                    ...colorsArray[f],
                }

                // for each column
                for (let c = 0, lc = chart.columns.length; c<lc; c++) {
                    // get the value
                    dataset.data.push( parseValue(features[f].properties[chart.columns[c][1]]) )
                }

                // split datasets if pyramid
                if (chart.type === 'piramide') {
                    // split dataset in half
                    let halfDatasetData = dataset.data.splice(0, Math.ceil(dataset.data.length / 2))
                    // invert values of first half
                    halfDatasetData = halfDatasetData.map(n => n *= -1)
                    // revert values so children goes on the bottom/end
                    halfDatasetData.reverse()
                    dataset.data.reverse()
                    // push it to datasets array
                    let halfDataset = {
                        ...dataset,
                        label: 'Homens',
                        ...colorsArray[2],
                        data: halfDatasetData,
                    }
                    dataObj.datasets.push(halfDataset)
                    // change other half
                    dataset.label = 'Mulheres'
                    dataset = {
                        ...dataset,
                        ...colorsArray[0],
                    }
                }

                // push it to datasets array
                dataObj.datasets.push(dataset)
            }
        }

        // if dataset is pyramid, lose half of labels and reverse them
        if (chart.type === 'piramide') {
            dataObj.labels.splice(0, Math.ceil(dataObj.labels.length / 2))
            dataObj.labels.reverse()
        }

        // if dataset is pie, change items colors' to blue/red/green
        if (chart.type === 'pizza') {
            dataObj.datasets[0].backgroundColor = [
                colorsArray[2].backgroundColor,
                colorsArray[0].backgroundColor,
                colorsArray[1].backgroundColor,
                colorsArray[3].backgroundColor,
                colorsArray[4].backgroundColor,
                colorsArray[5].backgroundColor,
            ]
            dataObj.datasets[0].borderColor = [
                colorsArray[2].borderColor,
                colorsArray[0].borderColor,
                colorsArray[1].borderColor,
                colorsArray[3].borderColor,
                colorsArray[4].borderColor,
                colorsArray[5].borderColor,
            ]
            dataObj.datasets[0].hoverBackgroundColor = [
                colorsArray[2].hoverBackgroundColor,
                colorsArray[0].hoverBackgroundColor,
                colorsArray[1].hoverBackgroundColor,
                colorsArray[3].hoverBackgroundColor,
                colorsArray[4].hoverBackgroundColor,
                colorsArray[5].hoverBackgroundColor,
            ]
            dataObj.datasets[0].hoverBorderColor = [
                colorsArray[2].hoverBorderColor,
                colorsArray[0].hoverBorderColor,
                colorsArray[1].hoverBorderColor,
                colorsArray[3].hoverBorderColor,
                colorsArray[4].hoverBorderColor,
                colorsArray[5].hoverBorderColor,
            ]
        }

        return dataObj
    }

    const chartJSX = (chart, features) => {
        if (chart.type === 'barra') {
            return <Bar data={dataObject(chart, features)}/>
        }
        if (chart.type === 'barra-horizontal') {
            return <HorizontalBar data={dataObject(chart, features)} options={{
                scales: {
                    xAxes: [
                        {
                            ticks: {
                                beginAtZero: true,
                            },
                        },
                    ]
                },
            }}/>
        }
        if (chart.type === 'linha') {
            return <Line data={dataObject(chart, features)}/>
        }
        if (chart.type === 'pizza') {
            return <Pie data={dataObject(chart, features)}/>
        }
        if (chart.type === 'piramide') {
            return <HorizontalBar
                data={dataObject(chart, features)}
                width={353}
                height={350}
                options={{
                    scales: {
                        xAxes: [
                            {
                                display: false,
                            },
                        ],
                        yAxes: [
                            {
                                stacked: true,
                            },
                        ],
                    }
                }}
            />
        }
        return <p>Gráfico com tipo não suportado.</p>
    }

    return (
        <div className="charts">
            {layer && layer.charts.length > 0 ? <h3>Gráficos</h3> : ''}
            {
                layer && layer.charts ? layer.charts.map((chart, indexChart) => {
                    return (
                        <div key={indexChart} className="chart">
                            <p>{chart.title}</p>
                            {chartJSX(chart, layer.features)}
                        </div>
                    )
                }) : null
            }
        </div>
    )
}

export default Charts


<|start_filename|>src/components/Help/Help.js<|end_filename|>
import React from 'react'
import { Steps } from 'intro.js-react'

import 'intro.js/introjs.css';

let introjsOptions = {
    initialStep: 0,
    steps: [
        {
            element: '.toolbar-item.fa-question',
            intro: 'Bem-vindo ao inLoco 2.0! Este passo a passo irá mostrar rapidamente as funcionalidades da plataforma.',
        },
        {
            element: '.application-header--menu-button',
            intro: 'Menu: Aqui você pode escolher múltiplas camadas para exibição no mapa, através de categorias ou busca textual.',
        },
        {
            element: '.leaflet-control-zoom',
            intro: 'Para ajustar a área exibida, você pode arrastar, e dar duplo clique ou usar a rodinha do mouse (botão do meio) para alterar o zoom. Você também pode arrastar segurando shift para dar zoom em uma região específica.',
        },
        {
            element: '.platform-toolbar',
            intro: 'Aqui você encontra diferentes ferramentas: zoom/destaque em uma área específica, busca por polígono, desenho livre/destaque no mapa, compartilhar e download.',
        },
        {
            element: '.toolbar-item.login-logout',
            intro: 'Caso você seja membro ou servidor do MPRJ, poderá entrar com seu usuário e senha e ver camadas restritas ao público.',
        },
        {
            element: '.toolbar-item.search',
            intro: 'Busca detalhada: aqui você pode buscar por municípios ou bairros, destacando-os no mapa.',
        },
        {
            element: '.toolbar-item.polygonRequest',
            intro: 'Busca por área: aqui você pode obter informações sobre um recorte desenhado no mapa: pirâmide etária, quantidade de escolas etc.',
        },
        {
            element: '.toolbar-item.draw',
            intro: 'Desenho livre: aqui você pode inserir marcadores e polígonos no mapa, afim de destacar um determinado local.',
        },
        {
            element: '.toolbar-item.share',
            intro: 'Compartilhar: Clique aqui para copiar uma URL para esta visão do mapa.',
        },
        {
            element: '.toolbar-item.download',
            intro: 'Baixar: Clique aqui para baixar o mapa visualizado em tela em diferentes formatos.',
        },
        {
            element: '.toolbar-item.about',
            intro: 'Sobre: Clique aqui para ver informações gerais sobre a plataforma.',
        },
        {
            element: '.map-toolbar',
            intro: 'Aqui você encontra mais ferramentas: Google Street View™, busca por endereços ou pontos de referência, e mudança de mapa base.',
        },
        {
            element: '.toolbar-item.streetView',
            intro: 'Street View™: Clique aqui para ter uma visão panorâmica em 360° a nível do chão das ruas registradas nesta ferramenta do Google.',
        },
        {
            element: '.toolbar-item.searchStreet',
            intro: 'Busca de ruas: Clique aqui para buscar endereços ou pontos de interesse, para levar o mapa diretamente para o local desejado.',
        },
        {
            element: '.toolbar-item.basemaps',
            intro: 'Mapas de fundo: Clique aqui para alterar a camada de fundo, trocando entre imagens de satélite, terreno e mapas do Google Maps™ e OpenStreetMap.',
        },
    ],
    options: {
        hidePrev: true,
        hideNext: true,
        showBullets: false,
        showProgress: true,
        nextLabel: 'Próximo →',
        prevLabel: '← Anterior',
        skipLabel: 'Sair',
        doneLabel: 'Pronto!',
    }
}

const Help = ({ showHelp, onIntrojsExit }) => {

    const handleIntrojsExit = () => {
        onIntrojsExit()
    }

    return (
        <Steps
            enabled={showHelp}
            steps={introjsOptions.steps}
            initialStep={introjsOptions.initialStep}
            options={introjsOptions.options}
            onExit={handleIntrojsExit}
        />
    )
}

export default Help


<|start_filename|>src/components/App/mocks/baseMapsMock.js<|end_filename|>
const BASE_MAPS_MOCK = [
    {
        name: 'esri-light',
        image: require('../../../assets/img/basemap-light.png'),
        subtitle: 'Fundo claro (ESRI Light Gray Canvas)',
    },
    {
        name: 'esri-dark',
        image: require('../../../assets/img/basemap-dark.png'),
        subtitle: 'Fundo escuro (ESRI Dark Gray Canvas)',
    },
    {
        name: 'osm',
        image: require('../../../assets/img/basemap-osm.png'),
        subtitle: 'OpenStreetMap',
    },
    {
        name: 'esri-terrain',
        image: require('../../../assets/img/basemap-terrain.png'),
        subtitle: 'Terreno (ESRI World Terrain)'
    },
    {
        name: 'esri-satellite',
        image: require('../../../assets/img/basemap-satellite.png'),
        subtitle: 'Imagens de Satélite (ESRI World Imagery)'
    },
]

export default BASE_MAPS_MOCK


<|start_filename|>src/components/LeafletMap/LeafletMap.js<|end_filename|>
import React from 'react'
import Leaflet from 'leaflet'
import { EditControl } from 'react-leaflet-draw'
import Proj4 from 'proj4'
import { Map, WMSTileLayer, TileLayer, Marker, Popup, ZoomControl, ScaleControl, FeatureGroup, LayersControl, ImageOverlay } from 'react-leaflet'

const { BaseLayer, Overlay } = LayersControl
const GOOGLE_API_TOKEN = '<KEY>'
const BASEMAP_URL = {
    OPENSTREETMAP:    'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
    ESRI_DARK:      `https://services.arcgisonline.com/arcgis/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}`,
    ESRI_LIGHT:     `https://services.arcgisonline.com/arcgis/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}`,
    ESRI_TERRAIN:   `https://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}`,
    ESRI_SATELLITE: `https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}`,
}
const MIN_ZOOM = 6

Leaflet.Icon.Default.imagePath = '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/'

const LeafletMap = ({
    mapProperties,
    showMenu,
    showSidebarRight,
    layers,
    showDrawControls,
    orderByLayerOrder,
    toolbarActive,
    showSearchPolygon,
    showPolygonDraw,
    handleMapClick,
    handleMapMove,
    onUpdateBasemapLoadingStatus,
    onDraw,
    onStreetViewHide,
    onPolygonDelete,
}) => {

    const isCluster = (layer) => {
        return layer.id.indexOf('cluster') >= 0
    }

    let clientHeight = document.body.clientHeight
    let clientWidth = document.body.clientWidth

    let point = Leaflet.point(clientWidth, clientHeight)

    const availableBasemaps = ['osm', 'esri-light', 'esri-dark', 'esri-terrain', 'esri-satellite']

    // if basemap has changed, i should update it *once*
    if (mapProperties && mapProperties.currentMap && !mapProperties.currentMap.loadDone) {
        // get (hidden) layers control box
        let basemapFormElements = document.querySelectorAll('.leaflet-control-layers-selector')
        let basemapIndex
        for (var i=0, l=availableBasemaps.length; i<l; i++) {
            // get the index of the basemap i want to change to
            if (mapProperties.currentMap.name === availableBasemaps[i] ) {
                basemapIndex = i
            }
        }

        // trigger desired basemap select form
        basemapFormElements[basemapIndex].click()

        // trigger background layer (last checkbox) twice to switch if off and on again to put it on the top
        basemapFormElements[basemapFormElements.length-1].click()
        basemapFormElements[basemapFormElements.length-1].click()

        // dispatch action to say it is done
        onUpdateBasemapLoadingStatus()
    }

    // projections
    const firstProjection = 'GOOGLE'
    const secondProjection = 'WGS84'

    // initial position and zoom
    const position          = mapProperties && mapProperties.initialCoordinates ? [mapProperties.initialCoordinates.lat, mapProperties.initialCoordinates.lng] : [0,0]
    const zoom              = mapProperties && mapProperties.initialCoordinates && mapProperties.initialCoordinates.zoom ? mapProperties.initialCoordinates.zoom : 10
    var   placeToCenter     = mapProperties && mapProperties.placeToCenter ? mapProperties.placeToCenter : undefined
    var   googleSearchCoord = mapProperties && mapProperties.googleSearchCoord ? mapProperties.googleSearchCoord : undefined
    var   bounds
    var   opacity           = mapProperties && mapProperties.opacity !== undefined ? mapProperties.opacity : .5
    var   contour           = mapProperties && mapProperties.contour !== undefined ? mapProperties.contour : 'borda'
    var   color             = 'preto'
    let imageBounds         = mapProperties && mapProperties.currentCoordinates !== undefined ? mapProperties.currentCoordinates.bounds : null

    const regionStyle       = `plataforma:busca_regiao_${contour}_${color}`

    if (placeToCenter) {
        bounds = placeToCenter.geom.split(',')

        var west = parseInt(bounds[0])
        var east = parseInt(bounds[2])
        var south = parseInt(bounds[3])
        var north = parseInt(bounds[1])

        var prj1 = Proj4(firstProjection, secondProjection, [east, south])
        var prj2 = Proj4(firstProjection, secondProjection, [west, north])
        bounds = [[prj1[1], prj2[0]], [prj2[1], prj1[0]]]
    } else if (googleSearchCoord) {
        bounds = googleSearchCoord
    }

    var CQL_FILTER

    // This function gets the code to fill CQL filter
    const getCode = place => {
        var cd
        var operator = ' = '
        if (contour === 'opaco') {
            operator = ' <> '
        }
        switch (place.tipo) {
            case 'CRAAI':
                cd = 'cod_craai' + operator + place.cd_craai
                break
            case 'MUNICIPIO':
                cd = 'cod_mun' + operator + place.cd_municipio
                break
            case 'BAIRRO':
                cd = 'cod_bairro' + operator + place.cd_bairro
                break
            case 'CI':
                cd = 'cod_ci' + operator + place.cd_ci
                break
            case 'PIP':
                cd = 'cod_pip' + operator + place.cd_pip
                break
            case 'ORGAO':
                operator = ' = '
                cd = 'cd_orgao' + operator + place.cd_orgao
                break
            default:
                cd = undefined
        }
        return cd
    }

    if (placeToCenter) {
        CQL_FILTER = `tipo='${placeToCenter.tipo}' and ${getCode(placeToCenter)}`
        if (placeToCenter.tipo === 'ORGAO'){
            CQL_FILTER = `tipo='NEGATIVO' and ${getCode(placeToCenter)}`
        }
    }

    const getLayerCQLFilterParameter = () => {
        let param = 'cod_'
        if (placeToCenter.tipo === 'ORGAO') {
            param = 'cd_'
        }
        if (placeToCenter.tipo === 'MUNICIPIO') {
            return param += 'mun'
        }

        if (placeToCenter.tipo === 'ORGAO') {
            param = 'cd_'
        }

        return param += placeToCenter.tipo.toLowerCase()
    }

    const getLayerCQLFilterValue = () => {
        if (placeToCenter.tipo === 'ORGAO') {
            return placeToCenter.id
        }
        return `${placeToCenter['cd_'+placeToCenter.tipo.toLowerCase()]}`
    }

    const getLayerCQLFilter = ({layer, plateToCenter}) => {
        let CQLFilter = ''

        if (layer.filteredData && layer.filterKey && layer.filterValue) {
            CQLFilter = `strToLowerCase(${layer.filterKey}) LIKE '%${layer.filterValue}%'`
        }

        if (placeToCenter && placeToCenter.tipo !== 'ESTADO') {
            let geom = `'tipo=''${placeToCenter.tipo}'' and ${getLayerCQLFilterParameter()}=''${getLayerCQLFilterValue()}''`
            if (CQLFilter) {
                CQLFilter += ' AND '
            }
            CQLFilter += `INTERSECTS(geom, querySingle('plataforma:busca_regiao', 'geom', ${geom}'))`
        }

        if (CQLFilter === '') {
            return '1=1'
        }

        return CQLFilter
    }

    // Geoserver config
    const ENDPOINT = __API__
    const IMAGE_FORMAT = 'image/png'

    // map class, to reposition arrow and map credit on the bottom
    let leafletMapClassName = 'module-leafletMap'
    if (showMenu) {
        leafletMapClassName += ' sidebar-left-opened'
    }
    if (showSidebarRight) {
        leafletMapClassName += ' sidebar-right-opened'
    }

    let drawPolygonOptions
    if (showPolygonDraw) {
        drawPolygonOptions = {
            rectangle: false,
            polyline: false,
            circle: false,
            marker: false,
            circlemarker: false,
        }
    } else {
        drawPolygonOptions = {
            rectangle: false,
            polyline: false,
            circle: false,
            marker: false,
            circlemarker: false,
            polygon: false,
        }
    }
    const myHandleMapClick = e => {
        handleMapClick(e, layers, toolbarActive, GOOGLE_API_TOKEN)
    }

    const myHandleMapMove = e => {
        handleMapMove(e)
    }

    const handleOnDraw = (e, layer) => {
        var coordinates = layer.getLatLngs()
        onDraw(e, coordinates, layers)
    }

    const handleOnDelete = (e, layer) => {
        if (layer) {
            onPolygonDelete()
        }
    }

    const returnMapInnerComponents = () => {
        return (
            <div>
                <LayersControl position='bottomleft'>
                    <BaseLayer checked={false} name='OpenStreetMap'>
                        <TileLayer url={BASEMAP_URL.OPENSTREETMAP} attribution='OpenStreetMap'/>
                    </BaseLayer>
                    <BaseLayer checked={true} name='Fundo claro (ESRI Light Gray Canvas)'>
                        <TileLayer url={BASEMAP_URL.ESRI_LIGHT} attribution='ESRI'/>
                    </BaseLayer>
                    <BaseLayer checked={false} name='Fundo escuro (ESRI Dark Gray Canvas)'>
                        <TileLayer url={BASEMAP_URL.ESRI_DARK} attribution='ESRI'/>
                    </BaseLayer>
                    <BaseLayer checked={false} name='Terreno (ESRI World Terrain)'>
                        <TileLayer url={BASEMAP_URL.ESRI_TERRAIN} attribution='ESRI'/>
                    </BaseLayer>
                    <BaseLayer checked={false} name='Imagens de Satélite (ESRI World Imagery)'>
                        <TileLayer url={BASEMAP_URL.ESRI_SATELLITE} attribution='ESRI'/>
                    </BaseLayer>
                    <Overlay checked={true} name='fundo'>
                        {/*state highlight layer*/}
                        <WMSTileLayer
                            url={ENDPOINT}
                            layers={'plataforma:retangulo'}
                            styles={'plataforma:retangulo_'+color}
                            format={IMAGE_FORMAT}
                            transparent={true}
                            opacity={opacity}
                            isBaseLayer={false}
                            visibility={true}
                            tiled={true}
                            buffer={0}
                        />
                    </Overlay>
                    {/*active layers*/}
                    {orderByLayerOrder(layers).map((layer, index) => {

                        let layerOpacity = 1
                        layers.map(l => {
                            if (l.id !== layer.id && l.highlight) {
                                layerOpacity = .3
                            }
                        })

                        if (isCluster(layer)) {
                            let imageURL = `${ENDPOINT}?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&STYLES&LAYERS=${layer.layerName}&SRS=EPSG%3A4326&WIDTH=${clientWidth}&HEIGHT=${clientHeight}`
                            if (imageBounds) {
                                imageURL += `&BBOX=${imageBounds._southWest.lng}%2C${imageBounds._southWest.lat}%2C${imageBounds._northEast.lng}%2C${imageBounds._northEast.lat}`
                            } else {
                                // temp values if map hasn't updated yet on initialization
                                imageBounds = [[-46.9,-23.5],[-38.1,-21.0]]
                                imageURL += `&BBOX=-46.9%2C-23.5%2C-38.1%2C-21.0`
                            }
                            return (
                                <Overlay checked={true} name={layer.layerName} key={index}>
                                    <ImageOverlay
                                        bounds={imageBounds}
                                        url={imageURL}
                                        transparent={true}
                                        opacity={layerOpacity}
                                    />
                                </Overlay>
                            )
                        } else {
                            return (
                                <Overlay checked={true} name={layer.layerName} key={index}>
                                    <WMSTileLayer
                                        url={ENDPOINT}
                                        layers={layer.layerName}
                                        styles={layer.styles[layer.selectedLayerStyleId].name}
                                        format={IMAGE_FORMAT}
                                        transparent={true}
                                        CQL_FILTER = {getLayerCQLFilter({layer, placeToCenter})}
                                        opacity={layerOpacity}
                                    />
                                </Overlay>
                            )
                        }
                    })}
                    {/*region highlight layer*/}
                    {
                        placeToCenter
                        ?
                        <Overlay checked={true} name="region_highlight">
                            <WMSTileLayer
                                url={ENDPOINT}
                                layers={placeToCenter.cd_orgao ? 'plataforma:busca_regiao_com_negativo' : 'plataforma:busca_regiao'}
                                styles={regionStyle}
                                format={IMAGE_FORMAT}
                                transparent={true}
                                exibeLegenda={false}
                                opacity={opacity}
                                isBaseLayer={false}
                                visibility={true}
                                tiled={true}
                                buffer={0}
                                CQL_FILTER = {CQL_FILTER ? CQL_FILTER : '1=1'}
                            />
                        </Overlay>
                        :
                        null
                    }
                </LayersControl>

                {/*Other controls*/}
                <ScaleControl position="bottomleft"/>
                <ZoomControl position="bottomright"/>
                <FeatureGroup>
                    {showDrawControls === true ?
                        <EditControl
                            position='topright'
                            draw={{
                                circlemarker: false,
                            }}
                        />
                        :
                        null
                    }
                </FeatureGroup>

                <FeatureGroup>
                    {showSearchPolygon === true ?
                        <EditControl
                            position='topright'
                            onCreated={
                                e => {
                                    var layer = e.layer
                                    e.layer.setStyle({
                                        color: '#bada55'
                                    })
                                    handleOnDraw(e, layer)
                                }
                            }
                            onEdited={
                                e => {
                                    var layers = e.layers.getLayers()
                                    var layer = layers[layers.length-1]
                                    handleOnDraw(e, layer)
                                }
                            }
                            onDeleted={
                                e => {
                                    var layers = e.layers.getLayers()
                                    var layer = layers[layers.length-1]
                                    handleOnDelete(e, layer)
                                }
                            }
                            draw={drawPolygonOptions}
                        />
                        :
                        null
                    }
                </FeatureGroup>
            </div>
        )
    }

    // translate Leaflet Draw to portuguese
    if (L) {
        L.drawLocal.draw.toolbar.actions.text         = 'Cancelar'
        L.drawLocal.draw.toolbar.actions.title        = 'Cancelar desenho'
        L.drawLocal.draw.toolbar.finish.text          = 'Terminar'
        L.drawLocal.draw.toolbar.finish.title         = 'Terminar desenho'
        L.drawLocal.draw.toolbar.undo.text            = 'Desfazer'
        L.drawLocal.draw.toolbar.undo.title           = 'Apagar último ponto desenhado'
        L.drawLocal.draw.toolbar.buttons.polyline     = 'Inserir uma linha'
        L.drawLocal.draw.toolbar.buttons.polygon      = 'Inserir um polígono'
        L.drawLocal.draw.toolbar.buttons.rectangle    = 'Inserir um retângulo'
        L.drawLocal.draw.toolbar.buttons.circle       = 'Inserir um círculo'
        L.drawLocal.draw.toolbar.buttons.marker       = 'Inserir um marcador'
        L.drawLocal.draw.toolbar.buttons.circlemarker = 'Inserir um marcador circular'

        L.drawLocal.draw.handlers.circle.tooltip.start    = 'Clique e arraste para desenhar um círculo.'
        L.drawLocal.draw.handlers.circle.radius           = 'Raio'
        L.drawLocal.draw.handlers.marker.tooltip.start    = 'Clique no mapa para criar um marcador.'
        L.drawLocal.draw.handlers.polygon.tooltip.start   = 'Clique para começar a desenhar um polígono.'
        L.drawLocal.draw.handlers.polygon.tooltip.cont    = 'Clique para continuar a desenhar um polígono.'
        L.drawLocal.draw.handlers.polygon.tooltip.end     = 'Clique no primeiro ponto para terminar o polígono.'
        L.drawLocal.draw.handlers.polyline.error          = '<strong>Erro:</strong> linhas das pontas não podem se cruzar!'
        L.drawLocal.draw.handlers.polyline.tooltip.start  = 'Clique para começar a desenhar uma linha.'
        L.drawLocal.draw.handlers.polyline.tooltip.cont   = 'Clique para continuar a desenhar uma linha.'
        L.drawLocal.draw.handlers.polyline.tooltip.end    = 'Clique no último ponto para terminar a linha.'
        L.drawLocal.draw.handlers.rectangle.tooltip.start = 'Clique e arraste para desenhar um retângulo.'
        L.drawLocal.draw.handlers.simpleshape.tooltip.end = 'Solte o mouse para terminar o desenho.'

        L.drawLocal.edit.toolbar.actions.save.text      = 'Confirmar'
        L.drawLocal.edit.toolbar.actions.save.title     = 'Confirmar alterações'
        L.drawLocal.edit.toolbar.actions.cancel.text    = 'Cancelar'
        L.drawLocal.edit.toolbar.actions.cancel.title   = 'Cancelar edição, descartar alterações'
        L.drawLocal.edit.toolbar.actions.clearAll.title = 'Limpar'
        L.drawLocal.edit.toolbar.actions.clearAll.text  = 'Limpar todos os desenhos'
        L.drawLocal.edit.toolbar.buttons.edit           = 'Editar desenhos'
        L.drawLocal.edit.toolbar.actions.editDisabled   = 'Nenhum desenho para editar'
        L.drawLocal.edit.toolbar.actions.remove         = 'Apagar desenhos'
        L.drawLocal.edit.toolbar.actions.removeDisabled = 'Nenhum desenho para apagar'

        L.drawLocal.edit.handlers.edit.tooltip.text     = 'Arraste os quadradinhos para editar o desenho.'
        L.drawLocal.edit.handlers.edit.tooltip.subtext  = 'Clique em Cancelar para desfazer as mudanças.'
        L.drawLocal.edit.handlers.remove.tooltip.text   = 'Clique em um desenho para remover.'

        if (L.drawLocal.format) {
            L.drawLocal.format.numeric.delimiters.thousands = '.'
            L.drawLocal.format.numeric.delimiters.decimal   = ','
        }
    }

    const returnMapWithCenter = () => {
        return (
            <Map
                center={position}
                zoom={zoom}
                zoomControl={false}
                minZoom={MIN_ZOOM}
                onClick={myHandleMapClick}
                onMoveend={myHandleMapMove}
            >
                {returnMapInnerComponents()}
            </Map>
        )
    }

    const returnMapWithBounds = () => {
        return (
            <Map
                bounds={bounds}
                zoomControl={false}
                minZoom={MIN_ZOOM}
                onClick={myHandleMapClick}
                onMoveend={myHandleMapMove}
            >
                {returnMapInnerComponents()}
            </Map>
        )
    }
    return (
        <div className={leafletMapClassName}>
            {
                bounds
                ? returnMapWithBounds()
                : returnMapWithCenter()
            }
        </div>
    )
}

export default LeafletMap


<|start_filename|>src/components/Modal/Contents/About.js<|end_filename|>
import React from 'react'

const About = () => {

    return (
        <div className="about-container">
            <p>O inLoco 2.0 é uma plataforma de mapas interativos criada pelo <a href="http://www.mprj.mp.br/" target="_blank">Ministério Público do Estado do Rio de Janeiro</a>, permitindo ao usuário visualizar e sobrepor dados geográficos de diversos assuntos, realizar buscas e dispor de diversas informações.</p>
            <p>Em caso de dúvidas na utilização do sistema, mande um email para <a href="mailto:<EMAIL>"><EMAIL></a>.</p>
            <p>Este sistema é software livre e seu código está disponibilizado no <a href="https://github.com/MinisterioPublicoRJ/inloco">GitHub</a>. Contribuições são bem-vindas! :)</p>
        </div>
    )
}

export default About


<|start_filename|>tests/Menu/Menu.test.js<|end_filename|>
import React from 'react';
import Menu from '../../src/components/Menu/Menu.js';

it('Menu component renders correctly with no data', () => {
    const component = shallow(<Menu/>);
    expect(component).toMatchSnapshot();
});

it('Menu component renders correctly with real data', () => {
    let mock = {
        "sidebarLeftWidth":37.859375,
        "sidebarLeftHeight":85.046875,
        "menuItems":[
            {
                "display":true,
                "id":"Assistência Social",
                "title":"Assistência Social",
                "layers":[1],
                "idMenu":1,
                "isSubMenu":false,
                "submenus":[],
                "selected":false,
                "match":true
            }
        ],
        "layers":[
            {
                "id":"plataforma_acterj_conselho_tutelar",
                "name":"acterj_conselho_tutelar",
                "title":"Conselho Tutelar",
                "workspace":"plataforma",
                "display":true,
                "restricted":false,
                "layerName":"plataforma:acterj_conselho_tutelar",
                "description":"Fontes: Associação dos Conselheiros Tutelares (ACTERJ). Contato: <EMAIL>/ http://www.acterj.org.br/ Ano:2016.",
                "bbox":"-44.71388499999997,-23.223640999999986,-41.045743999999964,-20.96493900000002",
                "caops":["infancia","cidadania","educacao"],
                "menu":"assistencia",
                "menu2":["Assistência Social"],
                "key":0,
                "styles":[
                    {
                        "id":0,
                        "title":"Conselho Tutelar",
                        "name":"plataforma:conselho_tutelar",
                        "description":"Conselhos Tutelares registrados na ACTERJ e CEDCA",
                        "icon":"/geoserver/plataforma/wms?service=WMS&version=1.1.0&request=GetMap&bbox=-44.71388499999997,-23.223640999999986,-41.045743999999964,-20.96493900000002&width=27&height=20&ssrs=EPSG:3857&format=image%2Fpng&layers=plataforma:acterj_conselho_tutelar&singleTile=true&styles=plataforma:conselho_tutelar",
                        "icon_2":"./resources/img/plataforma/icones/plataforma_acterj_conselho_tutelar-plataforma_conselho_tutelar_27_20.png","thumb":"/geoserver/plataforma/wms?service=WMS&version=1.1.0&request=GetMap&bbox=-44.71388499999997,-23.223640999999986,-41.045743999999964,-20.96493900000002&width=225&height=150&ssrs=EPSG:3857&format=image%2Fpng&layers=plataforma:acterj_conselho_tutelar&singleTile=true&styles=plataforma:conselho_tutelar",
                        "thumb_2":"./resources/img/plataforma/icones/plataforma_acterj_conselho_tutelar-plataforma_conselho_tutelar_225_150.png","tooltip":"<span style=\"width:750px;\"><img src=\"./resources/img/plataforma/icones/plataforma_acterj_conselho_tutelar-plataforma_conselho_tutelar_225_150.png\" onError=\"this.onerror=null;this.src='/geoserver/plataforma/wms?service=WMS&version=1.1.0&request=GetMap&bbox=-44.71388499999997,-23.223640999999986,-41.045743999999964,-20.96493900000002&width=225&height=150&ssrs=EPSG:3857&format=image%2Fpng&layers=plataforma:acterj_conselho_tutelar&singleTile=true&styles=plataforma:conselho_tutelar'\" width=\"150\" class=\"estilo-tooltip\"/><strong>Conselho Tutelar</strong><br>Conselho Tutelar</span>"
                    }
                ],
                "selected":false,
                "match":true,
                "showDescription":false,
                "selectedLayerStyleId":0
            }
        ],
        "currentLevel":0
    }

    const component = shallow(<Menu menuItems={mock.menuItems} layers={mock.layers} currentLevel={mock.currentLevel} sidebarLeftWidth={mock.sidebarLeftWidth} sidebarLeftHeight={mock.sidebarLeftHeight}/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>tests/Header/Header.test.js<|end_filename|>
import React from 'react';
import Header from '../../src/components/Header/Header.js';

it('Header component renders correctly with no data', () => {
    const component = shallow(<Header/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>src/actions/ToolbarActions.js<|end_filename|>
export const addGooglePlacesLatLong = latLong => {
    return {
        type: 'ADD_GOOGLE_PLACES_LAT_LONG',
        latLong,
    }
}

export const addPlaceLayer = item => {
    return {
        type: 'ADD_PLACE_LAYER',
        item,
    }
}

export const addTutelaLayer = item => {
    return {
        type: 'ADD_TUTELA_LAYER',
        item,
    }
}

export const changeActiveBaseMap = (baseMap) => {
    return {
        type: 'CHANGE_ACTIVE_BASE_MAP',
        baseMap,
    }
}

export const changeActiveToolbar = item => {
    return {
        type: 'CHANGE_ACTIVE_TOOLBAR',
        item,
    }
}

export const changeContour = item => {
    return {
        type: 'CHANGE_CONTOUR',
        item,
    }
}

export const changeGlobalFilterType = filterName => {
    return {
        type: 'CHANGE_GLOBAL_FILTER_TYPE',
        filterName,
    }
}

export const changeOpacity = item => {
    return {
        type: 'CHANGE_OPACITY',
        item,
    }
}

export const clearPlaceTutelaLayer = () => {
    return {
        type: 'CLEAR_PLACE_TUTELA_LAYER',
    }
}

export const searchPlaces = item => {
    return {
        type: 'SEARCH_PLACES',
        item,
    }
}

export const searchTutela = item => {
    return {
        type: 'SEARCH_TUTELA',
        item,
    }
}

export const togglePlace = item => {
    return {
        type: 'TOGGLE_PLACE',
        item,
    }
}

export const toggleTutela = item => {
    return {
        type: 'TOGGLE_TUTELA',
        item,
    }
}

export const activateDownloadLoader = () => {
    return {
        type: 'ACTIVATE_DOWNLOAD_LOADER',
    }
}

export const deactivateDownloadLoader = () => {
    return {
        type: 'DEACTIVATE_DOWNLOAD_LOADER',
    }
}


<|start_filename|>src/components/Menu/MenuItem.js<|end_filename|>
import React from 'react'
import Menu from './Menu'
import { withContentRect } from 'react-measure'

const MenuItem = withContentRect(['bounds'])(({
    measureRef,
    measure,
    contentRect,
    item,
    layers,
    onItemClick,
    onMouseOver,
    sidebarLeftWidth,
    sidebarLeftHeight,
    onMouseOut,
    onMenuItemClick,
    onLayerClick,
    parentMenuTitle,
    currentLevel,
    allMenuItems
}) => {
    // class name if menu item with children or single layer, with no children
    let menuItemClassName
    let visibleClass = ''
    let itemTitle = ''
    let otherIsSelected = false
    let otherIsNotMatched = false

    // if item is defined. can't be `if (item)` because item can be the numeric id `0`.
    if (item !== undefined) {
        menuItemClassName = item.title ? 'menu-item-with-children' : 'menu-item-layer'
        menuItemClassName += item.selected ? ' selected' : ''

        // check if one menu is selected, and no other items are matched, hide others
        if (allMenuItems) {
            for (let otherMenuItem of allMenuItems) {
                if (otherMenuItem.title && otherMenuItem.title !== item.title) {
                    if (otherMenuItem.selected) {

                        // we must not hide submenu children
                        let itemSubMenuFound = false

                        otherMenuItem.submenus.forEach( (subMenu) => {
                            if (item.idMenu === subMenu) {
                                itemSubMenuFound = true
                            }
                        })

                        if (!itemSubMenuFound) {
                            otherIsSelected = true
                        }

                    }
                    if (!otherMenuItem.match) {
                        otherIsNotMatched = true
                    }
                }
            }
            if (otherIsSelected && !otherIsNotMatched) {
                // we can only hide other items if not on search
                visibleClass = 'hidden'
            }
        }

        // if menu with children
        if (item.title) {
            itemTitle = item.title
            // if it doesn't match search, hide it
            if (!item.match) {
                visibleClass = 'hidden'
            }

            // if this menu has submenu opened, it should be visible
            item.submenus.forEach((subMenu) => {
                // check if this submenu is selected
                allMenuItems.forEach((oneMenuItem) => {
                    if (oneMenuItem.idMenu === subMenu) {
                        if (oneMenuItem.selected) {
                            // make it visible
                            visibleClass = ''
                        }
                    }
                })
            })

            // if this menu is selected
            if (item.selected) {
                // if this menu is a submenu it is active
                if (currentLevel === 1 || item.submenus) {
                    menuItemClassName += ' active'
                }
            }

        } else {
            // if it's a layer, check if it's match'ed.
            for (var i =0; i < layers.length; i++) {
                var layer = layers[i]
                if (layer.key === item) {
                    itemTitle = layer.title
                    if (!layer.match) {
                        visibleClass = 'hidden'
                    }
                }
            }

            // check if layer is selected
            if (!item.title){
                for (var i = 0; i < layers.length; i++) {
                    var layer = layers[i]
                    if (layer.key === item && layer.selected) {
                        menuItemClassName += ' layer-selected'
                    }
                }
            }
        }
        visibleClass += " menu-item-container"
    }

    // add class if on search
    if (otherIsNotMatched) {
        menuItemClassName += ' on-search'
    }

    function hasSubmenu() {
        if (!item.submenus) {
            return false
        }
        return item.submenus.length > 0 ? true : false
    }

    function handleItemClick(){
        // check if is a layer or a menu click
        if (item.id) {
            // is a menu
            // check if is selected
            if (!item.selected) {
                // if is not selected, do action
                return onItemClick(item)
            } else {
                // if is selected, check if my children is open
                if (hasSubmenu() && currentLevel > 1) {
                    return onItemClick(item)
                }
            }
        } else {
            // is a layer
            return onItemClick(layers[item])
        }
        return null
    }

    return (
        <div ref={measureRef} className={visibleClass}>
            <li
                onMouseOut={() => onMouseOut(item.id ? undefined : layers[item])}
                onMouseOver={
                    (e) => onMouseOver(item.id ? undefined : e,
                    layers[item],
                    sidebarLeftWidth,
                    contentRect.bounds.height,
                    contentRect.bounds.top,
                    contentRect.bounds.bottom
                )}
                onClick={() => handleItemClick()}
                className={menuItemClassName}
            >
                { itemTitle }
            </li>
            {
                (item && item.layers) ?
                <Menu
                    menuItems={item.layers}
                    menuTitle={item.title}
                    submenus={item.submenus}
                    allMenuItems={allMenuItems}
                    parentMenuTitle={parentMenuTitle}
                    key={item.idMenu}
                    idMenu={item.idMenu}
                    selected={item.selected}
                    layers={layers}
                    onMenuItemClick={onMenuItemClick}
                    onLayerClick={onLayerClick}
                    onMouseOver={onMouseOver}
                    sidebarLeftWidth={sidebarLeftWidth}
                    sidebarLeftHeight={sidebarLeftHeight}
                    onMouseOut={onMouseOut}
                    currentLevel={currentLevel}
                />
                : ''
            }
        </div>
    )
})
export default MenuItem


<|start_filename|>src/components/Sinalid/Sinalid.js<|end_filename|>
import React from 'react'
import {Pie, Bar} from 'react-chartjs-2'

const Sinalid = ({layer}) => {
    console.log('Sinalid layer', layer)

    // Empty stats
    let statInitialized = false
    let stats = {
        gender: {
            'MASCULINO': 0,
            'FEMININO': 0,
            'NÃO INFORMADO': 0,
        },
        ageRange: {
            '12 A 17 ANOS': 0,
            '18 A 24 ANOS': 0,
            '25 A 29 ANOS': 0,
            '30 A 34 ANOS': 0,
            '35 A 64 ANOS': 0,
            '65 ANOS OU MAIS': 0,
            'NÃO INFORMADA': 0,
        }
    }

    // Fill stats
    layer.features.map(feature => {
        feature.properties.sinalid ? feature.properties.sinalid.map(m => {
            statInitialized = true
            stats.gender[m.sexo]++
            stats.ageRange[m.faixa_IDADE]++
        }) : null
    })

    // Fill charts
    let genderChart = {
        labels: [
            'Masculino',
            'Feminino',
            '?',
        ],
        datasets: [
            {
                data: [
                    stats.gender['MASCULINO'],
                    stats.gender['FEMININO'],
                    stats.gender['NÃO INFORMADO'],
                ],
                backgroundColor: [
                    'rgba(0,0,255,.5)',
                    'rgba(255,0,0,.5)',
                    'rgba(0,0,0,.5)',
                ],
                hoverBackgroundColor: [
                    'rgba(0,0,255,1)',
                    'rgba(255,0,0,1)',
                    'rgba(0,0,0,1)',
                ],
            }
        ],
    }
    let ageRangeChart = {
        labels: [
            '12 a 17',
            '18 a 24',
            '25 a 29',
            '30 a 34',
            '35 a 64',
            '65+',
            '?',
        ],
        datasets: [
            {
                data: [
                    stats.ageRange['12 A 17 ANOS'],
                    stats.ageRange['18 A 24 ANOS'],
                    stats.ageRange['25 A 29 ANOS'],
                    stats.ageRange['30 A 34 ANOS'],
                    stats.ageRange['35 A 64 ANOS'],
                    stats.ageRange['65 ANOS OU MAIS'],
                    stats.ageRange['NÃO INFORMADA'],
                ],
                backgroundColor: [
                    'rgba(255,0,0,.5)',
                    'rgba(255,255,0,.5)',
                    'rgba(0,255,0,.5)',
                    'rgba(0,255,255,.5)',
                    'rgba(0,0,255,.5)',
                    'rgba(255,0,255,.5)',
                    'rgba(0,0,0,.5)',
                ],
                hoverBackgroundColor: [
                    'rgba(255,0,0,1)',
                    'rgba(255,255,0,1)',
                    'rgba(0,255,0,1)',
                    'rgba(0,255,255,1)',
                    'rgba(0,0,255,1)',
                    'rgba(255,0,255,1)',
                    'rgba(0,0,0,1)',
                ],
            },
        ],
    }

    // Remove empty values
    const removeEmptyValues = (obj) => {
        if (obj) {
            for (let i = obj.datasets[0].data.length - 1; i >= 0; i--) {
                if (obj.datasets[0].data[i] === 0) {
                    obj.labels.splice(i, 1)
                    obj.datasets[0].data.splice(i, 1)
                    obj.datasets[0].backgroundColor.splice(i, 1)
                    obj.datasets[0].hoverBackgroundColor.splice(i, 1)
                }
            }
        }
    }
    removeEmptyValues(genderChart)
    removeEmptyValues(ageRangeChart)

    let ageRangeChartOptions = {
        legend: {
            display: false
        },
        scales: {
            yAxes: [
                {
                    ticks: {
                        beginAtZero: true
                    }
                }
            ]
        },
    }

    console.log(statInitialized, stats)

    return (
        <div className="module-sinalid">
            <h3 className="layer-item-data--title">Sinalid - Desaparecidos</h3>
            {layer.features.map((feature, indexFeature) => {
                return (feature.properties.DP ?
                    <div key={indexFeature}>
                        <h4>{feature.properties.DP}ª DP: {feature.properties.sinalid ? feature.properties.sinalid.length : 'Não há'} desaparecidos</h4>
                        <h4>Gênero:</h4>
                        <Pie data={genderChart}/>
                        <h4>Idade:</h4>
                        <Bar data={ageRangeChart} options={ageRangeChartOptions}/>
                        <h4>Lista de pessoas:</h4>
                        {feature.properties.sinalid ? feature.properties.sinalid.map((missingPerson, indexMissingPerson) => {
                            return (
                                <div className="sinalid-card" key={indexMissingPerson}>
                                    <img className="sinalid-card--photo" src={'data:image/png;base64, ' + missingPerson.foto} alt={'Foto Desaparecido - ' + missingPerson.nome}/>
                                    <div className="sinalid-card--content">
                                        <div className="card-content-header">
                                        <p className="content-title">
                                            <i className="icon-sinalid icon-sinalid-user"></i>
                                            {missingPerson.nome}
                                        </p>
                                        <p className="content-date">
                                            Data de Desaparecimento:
                                            <span><i className="icon-sinalid icon-sinalid-calendar"></i>
                                            {missingPerson.desaparecimento}</span>
                                        </p>
                                        </div>
                                        <p className="card-content-text">{missingPerson.circunstancias}</p>
                                    </div>
                                </div>
                            )
                        }) : <p>Não há desaparecidos para esta delegacia.</p>}
                    </div>
                : null)
            })}
        </div>
    )
}

export default Sinalid


<|start_filename|>src/components/Tooltip/TooltipContainer.js<|end_filename|>
import React from 'react'
import Tooltip from './Tooltip'
import { connect } from 'react-redux'

const mapStateToProps = (state) => {
    return {
        tooltip: state.tooltip,
        scrollTop: state.scrollTop,
    }
}

const TooltipContainer = connect(
    mapStateToProps,
    null
)(Tooltip)

export default TooltipContainer


<|start_filename|>src/components/Toolbar/ToolbarContainer.js<|end_filename|>
import React from 'react'
import Toolbar from './Toolbar'
import { connect } from 'react-redux'
import {
    addPlaceLayer,
    addTutelaLayer,
    changeActiveBaseMap,
    changeActiveToolbar,
    changeContour,
    changeGlobalFilterType,
    changeOpacity,
    clearPlaceTutelaLayer,
    searchPlaces,
    searchTutela,
    togglePlace,
    toggleTutela,
    activateDownloadLoader,
    deactivateDownloadLoader,
} from '../../actions/ToolbarActions'
import {
    populateApp,
} from '../../actions/actions.js'
import ScaAPI from '../Api/ScaAPI.js'
import GeoAPI from '../Api/GeoAPI.js'

const mapStateToProps = (state, ownProps) => {
    return {
        baseMaps: state.baseMaps,
        globalFilterType: state.globalFilterType,
        layers: state.layers,
        loginStatus: state.loginStatus,
        mapProperties: state.mapProperties,
        places: state.places,
        showSidebarRight: state.showSidebarRight,
        toolbarActive: state.toolbarActive,
        tutela: state.tutela,
        ownProps,
    }
}

const mapDispatchToProps = dispatch => {
    const populateCallback = xmlData => {
        dispatch(populateApp(xmlData, location.hash))
    }
    const logoutCallback = data => {
        ScaAPI.logOutUser()
        GeoAPI.getContent(populateCallback)
    }
    return {
        onChangeActiveBaseMap: baseMap => {
            dispatch(changeActiveBaseMap(baseMap))
        },
        onClearPlaceTutelaLayer: () => {
            dispatch(clearPlaceTutelaLayer())
        },
        onContourChange: item => {
            dispatch(changeContour(item))
        },
        onGlobalFilterTypeChange: item => {
            dispatch(changeGlobalFilterType(item))
        },
        onKeyUpSearchPlaces: item => {
            dispatch(searchPlaces(item))
        },
        onKeyUpSearchTutela: item => {
            dispatch(searchTutela(item))
        },
        onOpacityChange: item => {
            dispatch(changeOpacity(item))
        },
        onPlaceClick: item => {
            dispatch(togglePlace(item))
            dispatch(addPlaceLayer(item))
        },
        onToolbarItemClick: item => {
            // wait a little bit for search component open
            window.setTimeout(() => {
                document.getElementById('searchField').focus()
            }, 100)
            dispatch(changeActiveToolbar(item))
            if (item === 'logout') {
                logoutCallback()
            }
        },
        onTutelaClick: item => {
            dispatch(toggleTutela(item))
            dispatch(addTutelaLayer(item))
        },
        onDownloadClick: () => {
            dispatch(activateDownloadLoader())
        },
        onDownloadEnd: () => {
            dispatch(deactivateDownloadLoader())
        }
    }
}

const ToolbarContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Toolbar)

export default ToolbarContainer


<|start_filename|>src/components/Loading/Loading.js<|end_filename|>
import React from 'react'

const Loading = ({ layers, showLoader, downloadLoader }) => {

    let loadingClass = 'module-loading'
    if (!showLoader && showLoader !== undefined) {
        loadingClass += ' hidden'
    }

    if (downloadLoader && downloadLoader === true) {
        loadingClass = 'module-loading'
    }

    return (
        <div className={loadingClass}>
            <div className="spinner">
                <i className="fa fa-spinner fa-spin" aria-hidden="true"></i><br />
                Carregando...</div>
        </div>
    )
}

export default Loading


<|start_filename|>copy_static.js<|end_filename|>
var chalk = require('chalk')
var cpx = require('cpx')

console.log(chalk.blue('Copying static files...'))

try {
    cpx.copySync('public/index.html', 'static/')
    cpx.copySync('public/favicon.ico', 'static/')
    cpx.copySync('public/icon-apple-touch-57x57.jpg', 'static/')
    cpx.copySync('public/icon-apple-touch-72x72.jpg', 'static/')
    cpx.copySync('public/icon-apple-touch-114x114.jpg', 'static/')
    cpx.copySync('public/icon-apple-touch-144x144.jpg', 'static/')
    cpx.copySync('public/og-img.jpg', 'static/')

    console.log(chalk.green('Job done, moving on'))
} catch (e) {
    console.log(chalk.red('An error occured: \n' + e))
}


<|start_filename|>src/components/GlobalFilter/GlobalFilter.js<|end_filename|>
import React from 'react'
import Place from './Place'


const GlobalFilter = ({
    globalFilterType,
    mapProperties,
    places,
    tutela,
    onClearPlaceTutelaLayer,
    onContourChange,
    onGlobalFilterTypeChange,
    onKeyUpSearchPlaces,
    onKeyUpSearchTutela,
    onOpacityChange,
    onPlaceClick,
    onTutelaClick,
}) => {

    // treat opacity tooltip value
    let opacity

    if (!mapProperties) {
        opacity = .5
    } else {
        opacity = mapProperties.opacity
    }
    if(isNaN(opacity)) {
        opacity = .5
    }
    opacity *= 100

    const handleClearPlaceTutelaLayer = e => {
        e.preventDefault()
        onClearPlaceTutelaLayer()
    }

    const handleGlobalFilterTypeChange = e => {
        onGlobalFilterTypeChange(e.target.value)
    }
    const handleKeyUpSearchPlaces = val => {
        onKeyUpSearchPlaces(val)
    }
    const handleKeyUpSearchTutela = val => {
        onKeyUpSearchTutela(val)
    }
    const handleOpacityChange = e => {
        onOpacityChange(e.target.value)
    }
    const handleTypeChange = e => {
        onContourChange(e.target.value)
    }
    const inputOnKeyUpPlaces = e => {
        handleKeyUpSearchPlaces(e.target.value)
    }
    const inputOnKeyUpTutela = e => {
        handleKeyUpSearchTutela(e.target.value)
    }

    return (
        <div>
            <form>
                <fieldset className="global-filter-wraptab">
                    <label className="global-filter-tab">
                        <input
                            id="places"
                            type="radio"
                            value="places"
                            checked={globalFilterType === 'places'}
                            onChange={handleGlobalFilterTypeChange}
                        />
                        <span htmlFor="places">Por área</span>
                    </label>

                    <label className="global-filter-tab">
                        <input
                            id="tutela"
                            type="radio"
                            value="tutela"
                            checked={globalFilterType === 'tutela'}
                            onChange={handleGlobalFilterTypeChange}
                        />
                        <span htmlFor="tutela">Por órgão</span>
                    </label>
                </fieldset>
            </form>
            <div className="global-filter">
                { globalFilterType === 'places' ?
                <div>
                    <form>
                        <fieldset className="global-filter-form">
                            <p className="global-filter-form--title">
                                Busca detalhada
                                <button className="global-filter-button-clean" onClick={handleClearPlaceTutelaLayer}>limpar busca</button>
                            </p>
                            <fieldset className="global-filter-form--inputsearch">
                                <input
                                    id="searchField"
                                    type="search"
                                    placeholder="Insira o nome da área"
                                    onKeyUp={inputOnKeyUpPlaces}
                                />

                                <label htmlFor="searchField"><i className="fa fa-search"></i></label>
                            </fieldset>
                            <fieldset className="global-filter-form--selectiontype">
                                <label>Tipo de seleção:</label>
                                <label htmlFor="selectionType1" className="input-checkopacity">
                                    <input
                                        name="selectionType"
                                        type="radio"
                                        id="selectionType1"
                                        value="borda"
                                        onClick={e => handleTypeChange(e)} />
                                    <span>Demarcada</span>
                                </label>
                                <label htmlFor="selectionType2" className="input-checkopacity">
                                    <input
                                        name="selectionType"
                                        type="radio"
                                        id="selectionType2"
                                        value="opaco"
                                        defaultChecked="checked"
                                        onClick={e => handleTypeChange(e)} />
                                    <span>Isolada</span>
                                </label>
                            </fieldset>

                            <fieldset className="global-filter-form--selectopacity">
                                <label>Opacidade da seleção:</label>
                                <label className="opacity-selector">
                                    <input type="range" min="0" max="10" defaultValue="5" onChange={e => handleOpacityChange(e)}></input>
                                    <span>{opacity}%</span>
                                </label>
                            </fieldset>
                        </fieldset>
                    </form>
                    <div className="global-filter-places">
                        <p className="global-filter-places--title">Áreas dos CRAAIs</p>
                        <div className="list-crais">
                            {places ? places.map(p => <Place onPlaceClick={onPlaceClick} key={p.id} place={p}/>) : null}
                        </div>
                    </div>
                </div> : null }
                { globalFilterType === 'tutela' ?
                <div>
                    <form>
                        <fieldset className="global-filter-form">
                            <p className="global-filter-form--title">
                                Busca detalhada
                                <button className="global-filter-button-clean" onClick={handleClearPlaceTutelaLayer}>limpar busca</button>
                            </p>
                            <fieldset className="global-filter-form--inputsearch">
                                <input
                                    id="searchField"
                                    type="search"
                                    placeholder="Insira o nome da área"
                                    onKeyUp={inputOnKeyUpTutela}
                                />

                                <label htmlFor="searchField"><i className="fa fa-search"></i></label>
                            </fieldset>
                            <fieldset className="global-filter-form--selectiontype">
                                <label>Tipo de seleção:</label>
                                <label htmlFor="selectionType1" className="input-checkopacity">
                                    <input
                                        name="selectionType"
                                        type="radio"
                                        id="selectionType1"
                                        value="borda"
                                        onClick={e => handleTypeChange(e)} />
                                    <span>Demarcada</span>
                                </label>
                                <label htmlFor="selectionType2" className="input-checkopacity">
                                    <input
                                        name="selectionType"
                                        type="radio"
                                        id="selectionType2"
                                        value="opaco"
                                        defaultChecked="checked"
                                        onClick={e => handleTypeChange(e)} />
                                    <span>Isolada</span>
                                </label>
                            </fieldset>

                            <fieldset className="global-filter-form--selectopacity">
                                <label>Opacidade da seleção:</label>
                                <label className="opacity-selector">
                                    <input type="range" min="0" max="10" defaultValue="5" onChange={e => handleOpacityChange(e)}></input>
                                    <span>{opacity}%</span>
                                </label>
                            </fieldset>
                        </fieldset>
                    </form>
                    <div className="global-filter-places">
                        <p className="global-filter-places--title">Áreas dos Órgãos</p>
                        <div className="list-crais">
                            {tutela ? tutela.map(p => <Place onPlaceClick={onTutelaClick} key={p.id} place={p}/>) : null}
                        </div>
                    </div>
                </div> : null }
            </div>
        </div>
    )
}

export default GlobalFilter


<|start_filename|>src/components/Menu/menuReducer.js<|end_filename|>
/**
* Creates menu array
* @param   {Object[]} layers Array of layers
* @returns {Object[]} Menu array with categories data, including layers IDs
*/
const menuReducer = (layers) => {
    const menu = []

    layers.forEach(layer => {
        if (layer.menu2) {
            // creates menu item if it doesn't exists
            const menuAlreadyExists = menu.some(menuItem => (layer.menu2[0] && menuItem.id === layer.menu2[0]))
            const subMenuAlreadyExists = menu.some(menuItem => (layer.menu2[1] && menuItem.id === layer.menu2[1]))

            // creates menu item if it doesn't exists
            if (!menuAlreadyExists && layer.menu2[0].trim()) {
                // create menu item
                menu.push({
                    display: true,
                    id: layer.menu2[0],
                    title: layer.menu2[0],
                    layers: [],
                    idMenu: menu.length,
                    isSubMenu: false,
                    submenus: [],
                });
            }

            // new submenu
            if (!subMenuAlreadyExists && layer.menu2[1] && layer.menu2[1].trim()) {
                // create submenu
                const thisSubmenuId = menu.length

                menu.push({
                    display: true,
                    id: layer.menu2[1],
                    title: layer.menu2[1],
                    layers: [],
                    idMenu: thisSubmenuId,
                    isSubMenu: true,
                    submenus: [],
                })

                // include my submenu id to father menu submenus array
                menu.forEach((menuItem) => {
                    // find my father
                    if (menuItem.id === layer.menu2[0]) {
                        // add to submenus array
                        menuItem.submenus.push(thisSubmenuId)
                    }
                })
            }

            // then add the layer ID to an array of its menu item
            menu.forEach((menuItem) => {
                // make sure it has no submenu
                if (layer.menu2.length === 1 && menuItem.id === layer.menu2[0]) {
                    // add layer to this menu layers array
                    menuItem.layers.push(layer.key)
                }

                // then add the layer ID to an array of its menu item
                if (layer.menu2.length === 2 && menuItem.id === layer.menu2[1]) {
                    // add layer to this submenu layers array
                    menuItem.layers.push(layer.key)
                }

            })
        }
    })

    // finally, sort menu categories in A-Z
    menu.sort((a, b) => {
        if (a.title < b.title) {
            return -1
        }
        if (a.title > b.title) {
            return 1
        }
        return 0
    })

    return menu
}

export default menuReducer


<|start_filename|>src/components/Toolbar/Toolbar.js<|end_filename|>
import React from 'react'
import ToolbarMenu from '../ToolbarMenu/ToolbarMenu'

const Toolbar = ({
    baseMaps,
    globalFilterType,
    layers,
    loginStatus,
    mapProperties,
    places,
    showSidebarRight,
    toolbarActive,
    tutela,
    onChangeActiveBaseMap,
    onClearPlaceTutelaLayer,
    onContourChange,
    onGlobalFilterTypeChange,
    onKeyUpSearchPlaces,
    onKeyUpSearchTutela,
    onOpacityChange,
    onPlaceClick,
    onToolbarItemClick,
    onTutelaClick,
    onDownloadClick,
    onDownloadEnd,
    ownProps,
}) => {
    let className
    let tooltipClassName = 'tooltip'
    let active
    let { type, items } = ownProps

    if (type === 'platform') {
        className = 'platform-toolbar'
        tooltipClassName += ' bottom'
    }
    if (type === 'map') {
        className = 'map-toolbar'
        tooltipClassName += ' top'
    }
    if (!items) {
        return null
    }
    if (showSidebarRight) {
        className += ' sidebar-left-opened'
    }

    if (toolbarActive) {
        active = toolbarActive
    }

    if (loginStatus) {
        items = items.map((item) => {
            if (item.name === 'login') {
                item.className = 'fa fa-sign-out login-logout login'
                item.tooltip = 'Logout'
                item.name = 'logout'
            }
            return item
        })
    } else {
        items = items.map(item => {
            if (item.name === 'logout') {
                item.className = 'fa fa-sign-in login-logout login'
                item.tooltip = 'Login'
                item.name = 'login'
            }
            return item
        })
    }

    function handleClick(e){
        if (e.target.classList.contains('toolbar-item')) {
            onToolbarItemClick(e.target.dataset.id)
        }
    }
    return (
        // the data-html2canvas-ignore attribute tells html2canvas to ignore rendering this element on image capture
        <div className={className} data-html2canvas-ignore={true}>
            {
                items.map( (item, index) => {
                    var itemClassName = 'toolbar-item ' + item.className
                    if (active === item.name) {
                        itemClassName += ' active'
                    }

                    // focus on searchStreet box manually
                    if (item.name === 'searchStreet' && active === 'searchStreet') {
                        setTimeout(() => {
                            document.getElementById('GooglePlacesSearch').focus()
                        }, 200)
                    }

                    return (
                        <div
                            data-id={item.name}
                            key={index}
                            className={itemClassName}
                            onClick={e => handleClick(e)}
                        >
                            <ToolbarMenu
                                active={active}
                                baseMaps={baseMaps}
                                globalFilterType={globalFilterType}
                                item={item}
                                layers={layers}
                                mapProperties={mapProperties}
                                places={places}
                                type={type}
                                tutela={tutela}
                                onChangeActiveBaseMap={onChangeActiveBaseMap}
                                onClearPlaceTutelaLayer={onClearPlaceTutelaLayer}
                                onContourChange={onContourChange}
                                onGlobalFilterTypeChange={onGlobalFilterTypeChange}
                                onKeyUpSearchPlaces={onKeyUpSearchPlaces}
                                onKeyUpSearchTutela={onKeyUpSearchTutela}
                                onOpacityChange={onOpacityChange}
                                onPlaceClick={onPlaceClick}
                                onToolbarItemClick={onToolbarItemClick}
                                onTutelaClick={onTutelaClick}
                                onDownloadClick={onDownloadClick}
                                onDownloadEnd={onDownloadEnd}
                                orderByLayerOrder={ownProps.orderByLayerOrder}
                            >
                            </ToolbarMenu>
                            <span className={tooltipClassName}>{item.tooltip}</span>
                        </div>
                    )
                })
            }
        </div>
    )
}

export default Toolbar


<|start_filename|>src/components/App/appReducer.js<|end_filename|>
import geoServerXmlReducer from './reducers/geoServerXmlReducer'
import menuReducer from '../Menu/menuReducer'
import placesMock from './mocks/placesMock.json'
import tutelaMock from './mocks/tutela.json'
import BASE_MAPS_MOCK  from './mocks/baseMapsMock'

const CRAAI = 'CRAAI'
const ESTADO_ID = '0'
const ENV_DEV = process.env.NODE_ENV === 'mock'
const LAYER_FILTER_REGEX = /\(.*\|.*\)/

const togglePlace = (place, id) => {
    if ((place.id === id) && id !== ESTADO_ID) {
        place.nodes.forEach((p) => {
            p.show = p.show ? !p.show : true
        })
        return place
    } else if (place.nodes.length > 0) {
        var placeFound = null

        for (var i = 0; placeFound === null && i < place.nodes.length; i++) {
            placeFound = togglePlace(place.nodes[i], id)
        }
        return placeFound
    }
    return null
}

const toggleTutela = (tutela, id) => {
    if ((tutela.id === id) && id !== ESTADO_ID) {
        tutela.nodes.forEach((p) => {
            p.show = p.show ? !p.show : true
        })
        return tutela
    } else if (tutela.nodes.length > 0) {
        var tutelaFound = null

        for (var i = 0; tutelaFound === null && i < tutela.nodes.length; i++) {
            tutelaFound = togglePlace(tutela.nodes[i], id)
        }
        return tutelaFound
    }
    return null
}

const searchPlaceById = (place, id) => {
    if (place.id === id) {
        return place
    } else if (place.nodes.length > 0) {
        var placeFound = null

        for (var i = 0; placeFound === null && i < place.nodes.length; i++) {
            placeFound = searchPlaceById(place.nodes[i], id)
        }
        return placeFound
    }
    return null
}

const hideRestrictedLayers = (layer, loggedStatus) => {
    // check if user is not logged
    if (!loggedStatus && layer.restricted) {
        return {
            ...layer,
            selected: false,
            match: false,
            showDescription: false,
            selectedLayerStyleId: 0,
        }
    } else {
        return layer
    }
}

const appReducer = (state = {}, action) => {
    switch (action.type) {
        case 'POPULATE_APP':
            // parse layers from GeoServer
            let originalLayers = geoServerXmlReducer(action.xmlData.xmlData)
            let places = placesMock
            let tutela = tutelaMock

            let layers = originalLayers
            let loginStatus = false

            let storedLoginStatus = localStorage.getItem('loginStatus')
            if (storedLoginStatus) {
                try {
                    storedLoginStatus = JSON.parse(storedLoginStatus)
                    loginStatus = storedLoginStatus

                } catch (e) {
                    console.error('stored loginStatus data is corrupt', e)
                }
            }

            layers = layers.map(l => {
                return hideRestrictedLayers(
                    {
                        ...l,
                        selected: false,
                        match: true,
                        showDescription: false,
                        selectedLayerStyleId: 0,
                    },
                    loginStatus
                )
            })

            let menuItems = menuReducer(layers)
            menuItems = menuItems.map(m => {
                return hideRestrictedLayers(
                    {
                        ...m,
                        selected: false,
                        match: true,
                    },
                    loginStatus
                )
            })

            let tooltip = {
                text: '',
                show: false,
                sidebarLeftWidth: 0,
                top: 0,
            }

            let showMenu = false
            let showSidebarRight = false
            // parse the querystring/hash, if present
            let coordinates = __INITIAL_MAP_COORDINATES__
            let currentMap
            let placeToCenter

            if (action.hash) {
                // drop the initial #
                let hashString = action.hash.replace('#', '')

                // split by each parameter
                let paramsObj = hashString.split('&').reduce((params, param) => {
                    let [key, value] = param.split('=')
                    params[key] = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : ''
                    // split layers
                    if (key === 'layers') {
                        params[key] = value.split(',')
                    }
                    return params
                }, {})

                // if we have valid lat, lng & zoom params
                if (paramsObj.lat && paramsObj.lng && paramsObj.zoom) {
                    coordinates = {
                        lat: parseFloat(paramsObj.lat) || 0,
                        lng: parseFloat(paramsObj.lng) || 0,
                        zoom: parseInt(paramsObj.zoom) || 0,
                    }
                }

                // if we have valid basemap param
                if (paramsObj.basemap) {
                    currentMap = {
                        name: paramsObj.basemap,
                    }
                }

                // if we have valid layers param
                if (paramsObj.layers) {
                    // open sidebars
                    showMenu = true
                    showSidebarRight = true

                    // for every active layer
                    paramsObj.layers.forEach((activeLayer, index) => {
                        // split layer and style
                        let activeLayerParams = activeLayer.split(':')
                        let activeLayerName = activeLayerParams[0]
                        let activeStyleName = activeLayerParams[1].replace(LAYER_FILTER_REGEX, '')
                        let activeLayerFilter = activeLayerParams[1].match(LAYER_FILTER_REGEX)

                        // find this active layer on layers array
                        layers = layers.map(l => {
                            let selected = l.selected
                            let order = null
                            let showInformation = undefined
                            let selectedLayerStyleId = l.selectedLayerStyleId

                            // and activate it
                            if (l.id === activeLayerName) {
                                selected = true
                                showInformation = true
                                order = index

                                // select chosen style
                                let selectedStyle = l.styles.filter(s => s.name === 'plataforma:' + activeStyleName)
                                selectedLayerStyleId = selectedStyle[0].id

                                // select filter for later loading
                                if (activeLayerFilter !== null && activeLayerFilter.length) {
                                    let activeLayerFilterParams = activeLayerFilter[0]
                                        .replace('(', '')
                                        .replace(')', '')
                                        .split('|')
                                    l.filterKey = activeLayerFilterParams[0]
                                    l.filterValue = activeLayerFilterParams[1]
                                }
                            }
                            return {
                                ...l,
                                selected,
                                order,
                                showInformation,
                                selectedLayerStyleId,
                            }
                        })
                    })
                }

                // if we have valid placeToCenter param
                if (paramsObj.orgao) {
                    let orgaoToCenter
                    tutela.map(estado => {
                        estado.nodes.map(tut => {
                            tut.nodes.map(orgao => {
                                if (orgao.id === paramsObj.orgao) {
                                    orgaoToCenter = orgao
                                }
                            })
                        })
                    })
                    if (orgaoToCenter) {
                        placeToCenter = orgaoToCenter
                    }
                }
                if (paramsObj.craai) {
                    let regionToCenter
                    places.map(estado => {
                        estado.nodes.map(craai => {
                            if (craai.cd_craai == paramsObj.craai) {
                                // found craai
                                if (paramsObj.municipio) {
                                    // there is municipio, keep looking
                                    craai.nodes.map(municipio => {
                                        if (municipio.cd_municipio == paramsObj.municipio) {
                                            // found municipio
                                            if (paramsObj.bairro) {
                                                // there is bairro, keep looking
                                                municipio.nodes.map(bairro => {
                                                    if (bairro.cd_bairro == paramsObj.bairro) {
                                                        // found bairro
                                                        regionToCenter = bairro
                                                    }
                                                })
                                            } else {
                                                // there is no bairro, focus on municipio
                                                regionToCenter = municipio
                                            }
                                        }
                                    })
                                } else {
                                    // there is no municipio, focus on craai
                                    regionToCenter = craai
                                }
                            }
                        })
                    })

                    if (regionToCenter) {
                        placeToCenter = regionToCenter
                    }
                }
                // or, we could have municipio and no craai
                if (!paramsObj.craai && paramsObj.municipio) {
                    let regionToCenter
                    places.map(estado => {
                        estado.nodes.map(craai => {
                            craai.nodes.map(municipio => {
                                if (municipio.cd_municipio == paramsObj.municipio) {
                                    // found municipio
                                    regionToCenter = municipio
                                }
                            })
                        })
                    })

                    if (regionToCenter) {
                        placeToCenter = regionToCenter
                    }
                }
            }

            const DEFAULT_MAP = {
                name: 'esri-light',
            }

            let baseMaps = BASE_MAPS_MOCK

            let storedBaseMap = localStorage.getItem('lastBaseMap')
            if (storedBaseMap) {
                try {
                    let storedBaseMapObject = JSON.parse(storedBaseMap)
                    if ( storedBaseMapObject.name && (baseMaps.filter(basemap => basemap.name === storedBaseMapObject.name)).length ) {
                        storedBaseMap = storedBaseMapObject
                    } else {
                        storedBaseMap = null
                    }
                } catch (e) {
                    console.error('stored lastBaseMap data is corrupt', e)
                }
            }

            let mapProperties = {
                contour: 'opaco',
                currentMap: storedBaseMap || currentMap || DEFAULT_MAP,
                initialCoordinates: coordinates,
                opacity: .5,
                placeToCenter,
            }
            var newsTimestamp = window.localStorage.getItem('newsTimestamp')
            var lastValidTimestamp = '1505847454072'

            // Object to be returned

            var _return = {
                currentLevel: 0,
                layers,
                menuItems,
                showMenu,
                showSidebarRight,
                tooltip,
                searchString: '',
                mapProperties,
                scrollTop: 0,
                places,
                tutela,
                baseMaps,
                showPolygonDraw: true,
                showLoader: false,
                showTooltipMenu: true,
                loginStatus,
                loginError: null,
                globalFilterType: 'places',
            }

            // Check if content from localstorage is equal to last timestamp
            if (newsTimestamp === lastValidTimestamp) {
                // Don't show news modal
                return {
                    ..._return,
                    newsModal: false,
                }
            }

            return {
                ..._return,
                newsModal: true,
                showModal: true,
            }

        case 'TOGGLE_LAYER':
            var newLayers = []
            var showSidebarRight = false
            newLayers = state.layers.map(l => layer(l, action, state.layers))
            for (var i = 0; i < newLayers.length; i++) {
                var l = newLayers[i]
                if (l.selected) {
                    showSidebarRight = true
                }
            }
            return {
                ...state,
                layers: newLayers,
                showSidebarRight,
            }
        case 'TOGGLE_LAYER_INFORMATION':
        case 'SLIDE_LEFT_STYLES':
        case 'SLIDE_RIGHT_STYLES':
            var newLayers = []
            newLayers = state.layers.map(l => layer(l, action, state.layers))
            return {
                ...state,
                layers: newLayers,
            }
        case 'SLIDE_LAYER_UP':
            var newLayers = state.layers

            var myPosition
            var arrayBiggerThanMe = []
            var immediatelyBiggerThanMe
            var auxOrder

            // find this item
            for (var i=0, l=newLayers.length; i<l; i++) {
                if (newLayers[i].id === action.id) {
                    myPosition = i
                }
            }
            // find items with order higher than this item
            for (var j=0; j<l; j++) {
                if (newLayers[j].order !== undefined && newLayers[j].order > newLayers[myPosition].order) {
                    arrayBiggerThanMe.push(j)
                }
            }
            // if there are items with order higher than this item
            if (arrayBiggerThanMe.length > 0) {
                // sort them
                arrayBiggerThanMe.sort(function(a, b) {
                    return newLayers[a].order - newLayers[b].order
                })
                // selects the first higher one
                immediatelyBiggerThanMe = arrayBiggerThanMe[0]
                // swap them
                auxOrder = newLayers[myPosition].order
                newLayers[myPosition].order = newLayers[immediatelyBiggerThanMe].order
                newLayers[immediatelyBiggerThanMe].order = auxOrder
            }

            return {
                ...state,
                layers: newLayers,
            }
        case 'SLIDE_LAYER_DOWN':
            var newLayers = state.layers;

            var myPosition
            var arraySmallerThanMe = []
            var immediatelySmallerThanMe
            var auxOrder

            // find this item
            for (var i=0, l=newLayers.length; i<l; i++) {
                if (newLayers[i].id === action.id) {
                    myPosition = i
                }
            }
            // find items with order smaller than this item
            for (var j=0; j<l; j++) {
                if (newLayers[j].order !== undefined && newLayers[j].order < newLayers[myPosition].order) {
                    arraySmallerThanMe.push(j)
                }
            }
            // if there are items with order smaller than this item
            if (arraySmallerThanMe.length > 0) {
                // sort them
                arraySmallerThanMe.sort(function(a, b) {
                    return newLayers[b].order - newLayers[a].order
                })
                // selects the first smaller one
                immediatelySmallerThanMe = arraySmallerThanMe[0]
                // swap them
                auxOrder = newLayers[myPosition].order
                newLayers[myPosition].order = newLayers[immediatelySmallerThanMe].order
                newLayers[immediatelySmallerThanMe].order = auxOrder
            }

            return {
                ...state,
                layers: newLayers,
            }
        case 'DROP_LAYER':
            var draggedPosition = action.draggedPosition
            var targetPosition = action.targetPosition
            var newLayers = state.layers
            for (var i = 0; i < newLayers.length; i++) {
                if (typeof newLayers[i].order === 'number' && newLayers[i].order === draggedPosition) {
                    // found dragged layer
                    if (targetPosition > draggedPosition) {
                        // UP
                        // Move others down
                        for (var k = 0; k < newLayers.length; k++) {
                            if (typeof newLayers[k].order === 'number' && (newLayers[k].order <= targetPosition && newLayers[k].order > draggedPosition)) {
                                // found layers that need to be changed
                                newLayers[k].order--
                            }
                        }
                    } else {
                        // DOWN
                        // Move others up
                        for (var k = 0; k < newLayers.length; k++) {
                            if (typeof newLayers[k].order === 'number' && (newLayers[k].order >= targetPosition && newLayers[k].order < draggedPosition)) {
                                // found layers that need to be changed
                                newLayers[k].order++
                            }
                        }
                    }
                    newLayers[i].order = targetPosition
                    break
                }
            }
            return {
                ...state,
                layers: newLayers,
            }
        case 'SELECT_LAYER_STYLE':
            var newLayers = []
            newLayers = state.layers.map(l => {
                if (l.id !== action.id) {
                    return l
                }
                return {
                    ...l,
                    selectedLayerStyleId: action.styleId,
                };
            })
            return {
                ...state,
                layers: newLayers,
            }
        case 'TOGGLE_MENU':
            let currentLevel = state.currentLevel
            var newLayers = []

            // make a copy of the state
            var newMenuItems = [...state.menuItems]

            // if i'm closing
            if (action.selected === true) {
                // find myself
                newMenuItems.forEach(menuItem => {
                    if (menuItem.id === action.id) {
                        // if i have children
                        if (menuItem.submenus.length > 0) {
                            // close my children
                            menuItem.submenus.forEach(submenu => {
                                // find this submenu in menus array
                                newMenuItems.forEach(thisMenuItem => {
                                    if (thisMenuItem.idMenu === submenu) {
                                        // close it
                                        thisMenuItem.selected = false
                                    }
                                })
                            })
                        } else {
                            // i don't have children, close myself
                            newMenuItems = newMenuItems.map(m => menuItem(m, action, state.currentLevel, state.loginStatus))
                        }
                    }
                })
            } else {
                // if i'm opening, do it right away
                newMenuItems = newMenuItems.map(m => menuItem(m, action, state.currentLevel, state.loginStatus))
            }
            if (action.selected) {
                currentLevel--
            } else {
                currentLevel++
            }
            return {
                ...state,
                currentLevel,
                menuItems: newMenuItems,
            };
        case 'UNTOGGLE_MENUS':
            var newMenuItems = state.menuItems.map(m => menuItem(m, action, null, state.loginStatus))
            return {
                ...state,
                currentLevel: 0,
                menuItems: newMenuItems,
            }
        case 'CLOSE_TOOLBARS':
            return {
                ...state,
                toolbarActive: null,
            }
        case 'SEARCH_LAYER':
            var newLayers = state.layers.map(l => searchLayer(l, action, state.loginStatus))
            var filteredLayers = newLayers.filter(layer => layer.match)
            var newMenuItems = []
            var searchString = action.text
            if (action.text === '') {
                // when emptying search, return all items
                newMenuItems = state.menuItems.map(m => {
                    return hideRestrictedLayers(
                        {
                            ...m,
                            match: true,
                            searchString,
                        },
                        state.loginStatus
                    )
                })
            } else {
                newMenuItems = state.menuItems.map(m => searchMenuItem(m, filteredLayers, state.menuItems, state.loginStatus))
            }
            return {
                ...state,
                layers: newLayers,
                menuItems: newMenuItems,
                searchString,
            }
        case 'CLEAN_SEARCH':
            return {
                ...state,
                searchString: '',
            }
        case 'SHOW_DESCRIPTION':
            var layerResult = state.layers.find(l => layer(l, action))
            var newTooltip
            if (layerResult) {
                newTooltip = {
                    text: layerResult.description,
                    show: true,
                    sidebarLeftWidth: action.sidebarLeftWidth,
                    // parentHeight: action.parentHeight,
                    // top: action.top,
                    mouseY: action.mouseY,
                }
            } else {
                newTooltip = {
                    text: '',
                    show: false,
                }
            }
            return {
                ...state,
                tooltip: newTooltip,
            }
        case 'HIDE_DESCRIPTION':
            return {
                ...state,
                tooltip: {
                    text: '',
                    show: false,
                }
            }
        case 'UPDATE_SCROLL_TOP':
            return {
                ...state,
                scrollTop: action.scrollTop
            }
        case 'SHOW_MENU_LAYER':
            return {
                ...state,
                showMenu: true,
                showTooltipMenu: false,
            }
        case 'HIDE_MENU_LAYER':
            return {
                ...state,
                showMenu: false,
            }
        case 'SHOW_SIDEBAR_RIGHT':
            return {
                ...state,
                showSidebarRight: true,
            }
        case 'HIDE_SIDEBAR_RIGHT':
            return {
                ...state,
                showSidebarRight: false,
            }
        case 'REMOVE_ALL_LAYERS':
            var newLayers = []
            newLayers = state.layers.map(l => {
                return {
                    ...l,
                    selected: false,
                    order: null,
                }
            })

            return {
                ...state,
                layers: newLayers,
                polygonData: null,
                showSidebarRight: false,
            }
        case 'POPULATE_STATE_WITH_LAYER_DATA':
            let returnedLayers = action.data
            var newLayers = state.layers

            newLayers = state.layers.map(l => {
                let features = null
                for (var i = 0; i < returnedLayers.length; i++) {
                    var returnedLayer = returnedLayers[i];
                    var returnedItems = returnedLayer.features
                    if (returnedItems && returnedItems.length > 0) {
                        let featureId = returnedItems[0].id.split('.')[0]
                        if (l.name === featureId) {
                            features = returnedItems
                        }
                    }

                }

                return {
                    ...l,
                    features,
                }
            })

            return {
                ...state,
                layers: newLayers,
            }

        case 'UPDATE_LAST_CLICK_DATA':
            return {
                ...state,
                lastClickData: action.data,
            }

        case 'LAST_MAP_POSITION':
            var mapProperties = {
                ...state.mapProperties,
                currentCoordinates: action.data,
            }

            return {
                ...state,
                mapProperties
            }

        case 'SHOW_STREET_VIEW':
            return {
                ...state,
                streetViewCoordinates: action.data,
            }

        case 'HIDE_STREET_VIEW':
            return {
                ...state,
                streetViewCoordinates: null,
                toolbarActive: null,
            }

        case 'GET_MODAL_DATA':
            var returnedItems = action.data.features
            var newLayers = state.layers

            var PAGE_SIZE = 5
            var pages = []

            // At least one element returned from the server
            if (returnedItems && returnedItems.length > 0) {

                // Removing string content after dot
                let featureId = returnedItems[0].id.split('.')[0]

                // split items into pages
                let returnedItemsCopy = JSON.parse(JSON.stringify(returnedItems))
                let returnedItemsCount = returnedItemsCopy.length

                while (returnedItemsCopy.length) {
                    pages.push(returnedItemsCopy.splice(0,PAGE_SIZE))
                }

                newLayers = state.layers.map(l => {
                    // extends modal object, if it exists
                    var modal = {}
                    if (l.modal) {
                        modal = {...l.modal}
                    }

                    if (l.name === featureId) {
                        modal.pages = pages
                        modal.currentPage = 0
                        modal.totalItemsCount = returnedItemsCount
                    }
                    return {
                        ...l,
                        modal,
                    }
                })
            }

            return {
                ...state,
                layers: newLayers,
            }

        case 'OPEN_MODAL':
            var showModal = true
            var currentModalLayer = action.layer
            var newLayers = state.layers
            var showExportFile = false

            newLayers = state.layers.map(l => {
                // extends modal object, if it exists
                var modal = {}
                if (l.modal) {
                    modal = {...l.modal}
                }

                // set to false
                modal.activeLayer = false

                // found my searched item
                if (l.id === currentModalLayer.id) {
                    // set to true
                    modal.activeLayer = true
                }

                return {
                    ...l,
                    modal,
                }
            })

            return {
                ...state,
                showModal,
                currentModalLayer,
                layers: newLayers,
            }

        case 'CLOSE_MODAL':
            var showModal = false
            var newsModal = false
            var showAbout = false
            var showLogin = false
            var toolbarActive = null
            var hideUpdates = document.getElementById('newsTimestamp')
            // set a timestamp from a hidden input from news modal on news modal
            if (hideUpdates) {
                window.localStorage.setItem('newsTimestamp', hideUpdates.dataset.value)
            }

            return {
                ...state,
                showModal,
                newsModal,
                showAbout,
                showLogin,
                toolbarActive,
                isFilterEmptyResult: false,
            }

        case 'OPEN_LAYER_FILTER_MODAL':
            var showModal = true
            var showLayerFilterModal = true

            return {
                ...state,
                showModal,
                showLayerFilterModal,
                modalLayerFilterName: action.layer.name,
                isFilterEmptyResult: false,
            }

        case 'LAYER_FILTER_LOADING':
            var newLayers = state.layers.map(l => {
                if (l.name === action.layer) {
                    return {
                        ...l,
                        filteredData: null,
                        filterKey: action.parameterKey,
                        filterValue: action.parameterValue,
                        isLoadingFilter: true,
                    }
                }
                return {...l}
            })
            return {
                ...state,
                isFilterEmptyResult: false,
                isLoadingFilter: true,
                layers: newLayers,
            }

        case 'LAYER_FILTER_LOADED':
            var layerName = null
            var isFilterEmptyResult = true
            if (action.data.features && action.data.features[0]) {
                layerName = action.data.features[0].id.replace(/\..*/g, '')
                isFilterEmptyResult = false
            }
            var newLayers = state.layers.map(l => {
                if (l.name === layerName) {
                    return {
                        ...l,
                        filteredData: action.data.features,
                        isLoadingFilter: false,
                    }
                }
                return {...l}
            })
            return {
                ...state,
                isLoadingFilter: false,
                isFilterEmptyResult,
                layers: newLayers,
            }

        case 'CLEAR_LAYER_FILTER':
        var newLayers = state.layers.map(l => {
            if (l.name === action.layer.name) {
                return {
                    ...l,
                    filteredData: null,
                    filterKey: null,
                    filterValue: null,
                }
            }
            return {...l}
        })
        return {
            ...state,
            layers: newLayers,
            isFilterEmptyResult: false,
            isLoadingFilter: false,
        }

        case 'CHANGE_ACTIVE_TAB':
            var clickedModalLayer = action.layer
            var newLayers = state.layers

            newLayers = state.layers.map(l => {
                // extends modal object, if it exists
                var modal = {}
                if (l.modal) {
                    modal = {...l.modal}
                }

                // set to false
                modal.activeLayer = false

                // found my searched item
                if (l.id === clickedModalLayer.id) {
                    // set to true
                    modal.activeLayer = true
                }

                return {
                    ...l,
                    modal,
                }
            })

            return {
                ...state,
                layers: newLayers,
            }

        case 'CHANGE_GLOBAL_FILTER_TYPE':
            return {
                ...state,
                globalFilterType: action.filterName,
            }

        case 'PAGINATE':
            var newLayers = state.layers

            newLayers = state.layers.map(l => {
                // extends modal object, if it exists
                var modal = {}
                if (l.modal) {
                    modal = {...l.modal}
                }

                // found my searched item
                if (l.id === action.layer.id) {
                    modal.currentPage = action.page
                }

                return {
                    ...l,
                    modal,
                }
            })

            return {
                ...state,
                layers: newLayers,
            }

        case 'CHANGE_ACTIVE_TOOLBAR':
            var toolbarActive = action.item
            if (toolbarActive === state.toolbarActive) {
                toolbarActive = undefined
            }

            // when the draw controls or polygon search opens or closes
            // the state should change
            // need to refactor because of repeated code
            var showDrawControls = state.showDrawControls === undefined ? false : state.showDrawControls
            var showSearchPolygon = state.showSearchPolygon === undefined ? false : state.showSearchPolygon
            var showHelp = state.showHelp === undefined ? false : state.showHelp
            var showAbout = state.showAbout === undefined ? false : state.showAbout
            var showModal = state.showModal === undefined ? false : state.showModal
            var showLogin = state.showLogin === undefined ? false : state.showLogin
            var loginStatus = state.loginStatus === undefined ? false : state.loginStatus
            let loginError = state.loginError === undefined ? false : state.loginError

            if (action.item === 'draw') {
                if (!state.showDrawControls) {
                    showSearchPolygon = false
                }
                showDrawControls = !state.showDrawControls
            } else if (state.toolbarActive === 'draw') {
                showDrawControls = false
            }

            if (action.item === 'polygonRequest') {
                if (!state.showSearchPolygon) {
                    showDrawControls = false
                }
                showSearchPolygon = !state.showSearchPolygon
            } else if (state.toolbarActive === 'draw') {
                showSearchPolygon = false
            }

            if (action.item === 'help') {
                if (!state.showHelp) {
                    showHelp = false
                }
                showHelp = !state.showHelp
            } else if (state.toolbarActive === 'help') {
                showHelp = false
            }

            if (action.item === 'about') {
                if (!state.showAbout) {
                    showAbout = false
                }
                showAbout = !state.showAbout
                if (showAbout) {
                    showModal = true
                } else {
                    showModal = false
                }
            } else if (state.toolbarActive === 'about') {
                showAbout = false
            }

            if (action.item === 'login') {
                if (!state.showLogin) {
                    showLogin = false
                }
                showLogin = !state.showLogin
                if (showLogin) {
                    showModal = true
                } else {
                    showModal = false
                }
            } else if (state.toolbarActive === 'login') {
                showLogin = false
            }

            if (action.item === 'logout') {
                loginError = false
                loginStatus = false
                toolbarActive = null
                localStorage.setItem('loginStatus', JSON.stringify(loginStatus))
            }

            return {
                ...state,
                toolbarActive,
                showDrawControls,
                showSearchPolygon,
                showHelp,
                showAbout,
                showModal,
                showLogin,
                loginError,
                loginStatus,
            }

        case 'TOGGLE_PLACE':
            var clickedPlace = action.item
            var currentPlace = state.mapProperties.placeToCenter
            var placeFound = null
            var id = clickedPlace.id
            var places = state.places.slice()
            var root = {
                id: 'root',
                nodes: places
            }

            placeFound = togglePlace(root, id);

            return {
                ...state,
                places,
            }

        case 'TOGGLE_TUTELA':
            var clickedPlace = action.item
            var currentPlace = state.mapProperties.placeToCenter
            var tutelaFound = null
            var id = clickedPlace.id
            var tutela = state.tutela.slice()
            var root = {
                id: 'root',
                nodes: tutela
            }

            tutelaFound = toggleTutela(root, id);

            return {
                ...state,
                tutela,
            }

        case 'ADD_PLACE_LAYER':
            var places = state.places.slice()
            var root = {
                id: 'root',
                nodes: places
            }
            var placeToCenter = searchPlaceById(root, action.item.id)
            var bounds = placeToCenter.geom.split(',')
            if ((state.bounds === bounds) || (state.toolbarActive !== 'search')) {
                placeToCenter = undefined
            }
            var mapProperties = {
                ...state.mapProperties,
                placeToCenter,
                googleSearchCoord: null,
            }
            return {
                ...state,
                mapProperties,
            }

        case 'ADD_TUTELA_LAYER':
            var tutela = state.tutela.slice()
            var root = {
                id: 'root',
                nodes: tutela
            }
            var placeToCenter = searchPlaceById(root, action.item.id)
            var bounds = placeToCenter.geom.split(',')
            if ((state.bounds === bounds) || (state.toolbarActive !== 'search')) {
                placeToCenter = undefined
            }
            var mapProperties = {
                ...state.mapProperties,
                placeToCenter,
                googleSearchCoord: null,
            }
            return {
                ...state,
                mapProperties,
            }

        case 'CLEAR_PLACE_TUTELA_LAYER':
            var places = state.places.slice()
            var tutela = state.tutela.slice()
            places[0].nodes.map(craai => {
                craai.nodes.map(municipio => {
                    delete municipio['show']
                })
                delete craai['show']
            })
            delete places[0]['search']
            tutela[0].nodes.map(tut => {
                tut.nodes.map(orgao => {
                    delete orgao['show']
                })
                delete tut['show']
            })
            delete tutela[0]['search']

            var mapProperties = {
                ...state.mapProperties,
                placeToCenter: null,
            }
            return {
                ...state,
                mapProperties,
                tutela,
                places
            }

        case 'CHANGE_OPACITY':
            var opacity = parseInt(action.item) / 10
            var mapProperties = {
                ...state.mapProperties,
                opacity,
            }
            return {
                ...state,
                mapProperties,
            }
        case 'CHANGE_CONTOUR':
            var contour = action.item
            var mapProperties = {
                ...state.mapProperties,
                contour,
            }
            return {
                ...state,
                mapProperties,
            }
        case 'SEARCH_PLACES':
            var globalFilterSearchPlaces = action.item
            var text = action.item.toLowerCase()
            var places = state.places.slice()

            for (let estado of places) {
                estado.show = false
                for (let craai of estado.nodes) {
                    craai.show = false
                    for (let municipio of craai.nodes) {
                        municipio.show = false
                        for (let bairro of municipio.nodes) {
                            bairro.show = false
                            if (text.length > 0 && bairro.title.toLowerCase().includes(text)) {
                                bairro.show = true
                                municipio.show = true
                                craai.show = true
                                estado.show = true
                            }
                        }
                        if (text.length > 0 && municipio.title.toLowerCase().includes(text)) {
                            municipio.show = true
                            craai.show = true
                            estado.show = true
                        }
                    }
                    if (craai.title.toLowerCase().includes(text)) {
                        craai.show = true
                        estado.show = true
                    }
                }
            }

            places[0].search = action.item
            return {
                ...state,
                places,
                globalFilterSearchPlaces,
            }

        case 'SEARCH_TUTELA':
            var globalFilterSearchTutela = action.item
            var text = action.item.toLowerCase()
            var tutela = state.tutela.slice()

            for (let estado of tutela) {
                estado.show = false
                for (let tut of estado.nodes) {
                    tut.show = false
                    for (let orgao of tut.nodes) {
                        orgao.show = false
                        if (text.length > 0 && orgao.title.toLowerCase().includes(text)) {
                            orgao.show = true
                            tut.show = true
                            estado.show = true
                        }
                    }
                    if (tut.title.toLowerCase().includes(text)) {
                        tut.show = true
                        estado.show = true
                    }
                }
            }

            tutela[0].search = action.item
            return {
                ...state,
                tutela,
                globalFilterSearchTutela,
            }

        case 'CHANGE_ACTIVE_BASE_MAP':
            var baseMap = action.baseMap
            var currentMap = state.mapProperties.currentMap
            var mapProperties = state.mapProperties
            currentMap = baseMap
            mapProperties = {
                ...mapProperties,
                currentMap
            }

            localStorage.setItem('lastBaseMap', JSON.stringify(currentMap))

            return {
                ...state,
                mapProperties,
            }

        case 'UPDATE_BASEMAP_LOADING_STATUS':
            var mapProperties = state.mapProperties
            var currentMap = mapProperties.currentMap
            currentMap = {
                ...currentMap,
                loadDone: true,
            }
            mapProperties = {
                ...mapProperties,
                currentMap
            }
            return {
                ...state,
                mapProperties,
            }
        case 'POPULATE_STATE_WITH_POLYGON_DATA':
            layers = action.data

            layers = layers.filter(l => {
                if (l.length > 0) {
                    return l
                }
            })

            let layerItems = layers.map((l) => {
                let object = {}
                if (l.length > 0) {
                    object = {
                        'category': l[0].category,
                        'items': l,
                    }
                    return object
                }
            })
            layerItems = layerItems.map(layerItem => {
                if (layerItem.category === 'População') {
                    layerItem.populacao_total = layerItem.items.reduce((acc, setor) =>{
                        return acc + setor.properties.População_Censo_2010
                    }, 0)
                    layerItem.domicilios_total = layerItem.items.reduce((acc, setor) =>{
                        return acc + setor.properties.Domicílios_Censo_2010
                    }, 0)

                    layerItem.piramide_total = {}
                    for (var i = 0; i < layerItem.items.length; i++) {
                        var item = layerItem.items[i];
                        var itemKeyPropertiesArray = Object.keys(item.properties)
                        for (var j = 0; j < itemKeyPropertiesArray.length; j++) {
                            var thisKey = itemKeyPropertiesArray[j]
                            var prefix = thisKey.substring(0,2)
                            if (prefix === 'h_' || prefix === 'm_') {
                                layerItem.piramide_total[thisKey] = (layerItem.piramide_total[thisKey] || 0) + item.properties[thisKey]
                            }

                        }

                    }
                }
                return layerItem
            })


            let polygonData = layerItems
            return {
                ...state,
                polygonData,
                showSidebarRight: true,
                showPolygonDraw: false,
                showLoader: false,
            }

        case 'REMOVE_POLYGON_DATA':
            let selectedLayers = state.layers.filter(l => l.selected)
            if (selectedLayers.length > 0) {
                showSidebarRight = true
            } else {
                showSidebarRight = false
            }
            return {
                ...state,
                polygonData: null,
                showSidebarRight,
                showPolygonDraw: true,
            }
        case 'START_POLYGON_DATA_REQUEST':
            return {
                ...state,
                showLoader: true,
            }
        case 'ADD_GOOGLE_PLACES_LAT_LONG':
            var mapProperties = state.mapProperties
            var latLong = action.latLong
            mapProperties = {
                ...mapProperties,
                googleSearchCoord: latLong,
                placeToCenter: null,
            }
            return {
                ...state,
                mapProperties,
            }

        case 'HIDE_HELP':
            return {
                ...state,
                showHelp: false,
                toolbarActive: null,
            }

        case 'LOGIN_USER':
            loginStatus = state.loginStatus
            loginError = false
            let showLogin = state.showLogin
            let showModal = state.showModal
            let toolbarActive = state.toolbarActive

            if (action.data.status === 200) {
                loginStatus = true
                loginError = null
                showModal = false
                showLogin = false
                toolbarActive = null
            } else {
                loginError = true
            }

            localStorage.setItem('loginStatus', JSON.stringify(loginStatus))

            return {
                ...state,
                loginStatus,
                loginError,
                showLogin,
                showModal,
                toolbarActive,
            }

        case 'CHECK_LOGGED_IN_USER':
            if (action.data.status === 200){
                loginStatus = true
                localStorage.setItem('loginStatus', JSON.stringify(loginStatus))
            } else {
                loginStatus = false
                localStorage.setItem('loginStatus', JSON.stringify(loginStatus))
            }
            return {
                ...state,
                loginStatus,
            }

        case 'ICON_MOUSE_OVER':
            var newLayers = state.layers
            newLayers.map(l => {
                if (l.id === action.id) {
                    l.highlight = true
                }
            })
            return {
                ...state,
                layers: newLayers,
            }

        case 'ICON_MOUSE_OUT':
            var newLayers = state.layers
            newLayers.map(l => {
                if (l.id === action.id) {
                    l.highlight = false
                }
            })
            return {
                ...state,
                layers: newLayers,
            }

        case 'SINALID_DATA':

            var newLayers = state.layers
            var sinalidData = action.data.data
            var dpNum = parseInt(sinalidData.delegacia.substr(0,3), 10)

            newLayers.map(l => {
                if (l.id === 'plataforma_inst_sinalid') {
                    l.features.map(f => {
                        if (f.properties.DP === dpNum) {
                            f.properties.sinalid = sinalidData.ld
                        }
                    })
                }
            })

        case 'ACTIVATE_DOWNLOAD_LOADER':

            return {
                ...state,
                downloadLoader: true,
            }

        case 'DEACTIVATE_DOWNLOAD_LOADER':

            return {
                ...state,
                downloadLoader: false,
            }

        case 'LOADING_PARAMS':
            var newLayers = state.layers.map(l => {
                if (l.id === action.id) {
                    return {
                        ...l,
                        isLoadingParams: true,
                    }
                }
                return {...l}
            })
            return {
                ...state,
                layers: newLayers,
            }

        case 'LOAD_PARAMS':
            var newLayers = state.layers.map(l => {
                if (l.id === action.id) {
                    return {
                        ...l,
                        isLoadingParams: false,
                        params: action.params,
                    }
                }
                return {...l}
            })
            return {
                ...state,
                layers: newLayers,
            }

        default:
            return state
    }
}

const layer = (layer, action, layers) => {
    switch (action.type) {
        case 'TOGGLE_LAYER':
            // close other layers' information panel
            if (layer.id !== action.id) {
                return {
                    ...layer,
                    showInformation: false,
                }
            }

            let order
            let features = layer.features || null
            let modal = layer.modal || null

            if (layer.selected) {
                // disabling layer
                // just remove order attribute
                order = null
                features = null
                modal = null
            } else {
                // enabling layer
                // find the biggest and return +1
                order = 0
                layers.map(l => {
                    if (typeof l.order === 'number') {
                        if (l.order > order) {
                            order = l.order
                        }
                    }
                })
                order++
            }

            return {
                ...layer,
                selected: !layer.selected,
                showInformation: true,
                order,
                features,
                modal,
            }
        case 'TOGGLE_LAYER_INFORMATION':
            if (layer.id !== action.id) {
                return layer
            }
            if (typeof layer.showInformation === 'undefined') {
                layer.showInformation = false // will be inverted on return
            }
            return {
                ...layer,
                showInformation: !layer.showInformation,
            }
        case 'TOGGLE_MENU':
            if (layer.id !== action.id) {
                return layer
            }
            return {
                ...layer,
                selected: !layer.match,
            }
        case 'SHOW_DESCRIPTION':
            if (layer.id === action.id) {
                return layer
            }
            return undefined;
        case 'HIDE_DESCRIPTION':
            if (layer.id !== action.id) {
                return layer
            }
            return {
                ...layer,
                showDescription: false,
            }
        case 'SLIDE_LEFT_STYLES':
            if (layer.id !== action.id) {
                return layer
            }
            var stylesPositionCounter = layer.stylesPositionCounter
            if (stylesPositionCounter !== 0) {
                stylesPositionCounter--
            }
            return {
                ...layer,
                stylesPositionCounter,
            }
        case 'SLIDE_RIGHT_STYLES':
            const STYLES_IN_A_ROW = 5
            if (layer.id !== action.id) {
                return layer
            }
            var stylesPositionCounter = layer.stylesPositionCounter || 0
            if (stylesPositionCounter < layer.styles.length - STYLES_IN_A_ROW) {
                stylesPositionCounter++
            }
            return {
                ...layer,
                stylesPositionCounter,
            }

    }
}

const menuItem = (menuItem, action, currentLevel, loginStatus) => {
    switch (action.type) {
        case 'TOGGLE_MENU':
            if (menuItem.id !== action.id) {
                return menuItem
            }
            return {
                ...menuItem,
                selected: !menuItem.selected,
            }
        case 'UNTOGGLE_MENUS':
            return hideRestrictedLayers(
                {
                    ...menuItem,
                    match: true,
                    selected: false,
                },
                loginStatus
            )

        default:
            return menuItem
    }
}

/**
 * @param {Array} layers - an array of layers
 * @param {Number} key - layer key to be found
 *
 * This function finds a layer by key
 *
 * @return {Object} layer object that was found
 */
const getLayerByKey = (layers, key) => {
    var returnLayer = undefined
    layers.forEach(function(layer) {
        if (layer.key === key) {
            returnLayer = layer
        }
    })
    return returnLayer
}

/**
 * @param {Array} menuItems - an array of menu items
 * @param {Number} id - submenu item id
 *
 * This function finds a menuItem by id
 *
 * @return {Object} menuItem object that was found
 */
const getMenuItemById = (menuItems, id) => {
    var returnMenuItem = undefined
    menuItems.forEach(function(menuItem) {
        if (menuItem.idMenu === id) {
            returnMenuItem = menuItem
        }
    })
    return returnMenuItem
}

/**
 * @param {Object} menuItem
 * @param {Array} layers
 *
 * This function returns a menuItem unchanged if
 * it is does not match the user search string. It
 * returns a menu item with match property changed to
 * true if it matches the user search string
 *
 * @return {Object} menuItem object that was found
 */
const searchMenuItem = (menuItem, layers, menuItems, loginStatus) => {
    var layerMatch = false
    menuItem.layers.forEach(function(menuItemLayer) {
        if (getLayerByKey(layers, menuItemLayer) !== undefined) {
            layerMatch = true
        }
    })

    if (menuItem.submenus.length > 0) {
        menuItem.submenus.forEach(function(menuItemSubmenu) {
            var submenuItem = getMenuItemById(menuItems, menuItemSubmenu)
            if (submenuItem !== undefined) {
                submenuItem.layers.forEach(function(submenuItemLayer) {
                    if (getLayerByKey(layers, submenuItemLayer) !== undefined) {
                        layerMatch = true
                    }
                })
            }
        })
    }

    if (layerMatch) {
        return hideRestrictedLayers(
            {
                ...menuItem,
                match: true,
                selected: true,
            },
            loginStatus
        )
    } else {
        return {
            ...menuItem,
            match: false,
        }
    }
}

const searchLayer = (layer, action, loginStatus) => {
    if (action.text === '') {
        return hideRestrictedLayers(
            {
                ...layer,
                match: true,
            },
            loginStatus
        )
    }

    if (layer.title.toLowerCase().includes(action.text.toLowerCase())) {
        return hideRestrictedLayers(
            {
                ...layer,
                match: true,
            },
            loginStatus
        )
    } else if (layer.description.toLowerCase().includes(action.text.toLowerCase())) {
        return hideRestrictedLayers(
            {
                ...layer,
                match: true,
            },
            loginStatus
        )
    } else {
        return {
            ...layer,
            match: false,
        }
    }
}

export default appReducer


<|start_filename|>src/components/Header/HeaderContainer.js<|end_filename|>
import React from 'react'
import Header from './Header'
import { connect } from 'react-redux'
import { showMenuLayer } from '../../actions/actions.js'

const mapStateToProps = (state) => {
    return {
        showTooltipMenu: state.showTooltipMenu,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onHeaderClick: () => {
            dispatch(showMenuLayer())
        }
    }
}

const HeaderContainer = connect(
    mapStateToProps,
    mapDispatchToProps,
)(Header)

export default HeaderContainer


<|start_filename|>src/components/HeaderRight/HeaderRightContainer.js<|end_filename|>
import React from 'react'
import HeaderRight from './HeaderRight'
import { connect } from 'react-redux'
import { showSidebarRight } from '../../actions/actions.js'

const mapStateToProps = (state) => {
    return {
        layers: state.layers,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onHeaderClick: () => {
            dispatch(showSidebarRight())
        }
    }
}

const HeaderRightContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(HeaderRight)

export default HeaderRightContainer


<|start_filename|>src/components/LayerStylesCarousel/LayerStyleItem.js<|end_filename|>
import React from 'react'

const LayerStyleItem = ({layer, style, index, onStyleClick}) => {

    let itemClassName = 'layer-styles-carousel--list-item'
    if (layer && layer.selectedLayerStyleId === index) {
        itemClassName += ' selected'
    }

    function styleClick() {
        return onStyleClick(layer, index)
    }

    return (
        <li className={itemClassName} onClick={styleClick}>
            { style ?
                <img className="layer-styles-carousel--image" src={style.thumb} alt={style.title}/>
            : '' }
        </li>
    )
}

export default LayerStyleItem


<|start_filename|>src/components/SidebarLeft/SidebarLeftContainer.js<|end_filename|>
import React from 'react'
import SidebarLeft from './SidebarLeft'
import { connect } from 'react-redux'
import { closeToolbars, searchLayer, cleanSearch, hideMenuLayer, untoggleAll } from '../../actions/actions.js'

const mapDispatchToProps = (dispatch) => {
    return {
        onSearchClick: () => {
            dispatch(closeToolbars())
        },
        onKeyUpSearch: (text) => {
            dispatch(untoggleAll())
            dispatch(searchLayer(text))
        },
        onBtnCleanSearch: () => {
            dispatch(cleanSearch())
            dispatch(searchLayer(''))
            dispatch(untoggleAll())
        },
        onClickMenuHeader: () => {
            dispatch(hideMenuLayer())
        }
    }
}

const mapStateToProps = (state) => {
    return {
        showMenu: state.showMenu,
        searchString: state.searchString,
    }
}

const SidebarLeftContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(SidebarLeft)

export default SidebarLeftContainer


<|start_filename|>src/components/LeafletMap/LeafletMapContainer.js<|end_filename|>
import React from 'react'
import LeafletMap from './LeafletMap'
import { connect } from 'react-redux'
import GeoAPI from '../Api/GeoAPI.js'
import SinalidAPI from '../Api/SinalidAPI.js'
import {
    populateStateWithLayerData,
    updateLastClickData,
    updateBasemapLoadingStatus,
    lastMapPosition,
    populateStateWithPolygonData,
    removePolygonData,
    startPolygonDataRequest,
    sinalidData,
} from '../../actions/actions.js'

const MAX_ITEMS_TO_LOAD = 3

const selectedLayers = (layers) => {
    if (!Array.isArray(layers)) {
        return []
    }
    return layers.filter(layer => layer.selected)
}

const mapStateToProps = (state, ownProps) => {
    return {
        mapProperties: state.mapProperties,
        showMenu: state.showMenu,
        showSidebarRight: state.showSidebarRight,
        layers: selectedLayers(state.layers),
        showDrawControls: state.showDrawControls,
        showSearchPolygon: state.showSearchPolygon,
        showPolygonDraw: state.showPolygonDraw,
        orderByLayerOrder: ownProps.orderByLayerOrder,
        places: state.places,
        toolbarActive: state.toolbarActive,
        streetViewCoordinates: state.streetViewCoordinates,
    }
}

const mapDispatchToProps = dispatch => {
    const onUpdateWithSelectedLayerData = layerData => {
        dispatch(populateStateWithLayerData(layerData))

        // Sinalid
        if (layerData[0]) {
            layerData[0].features.map(feature => {
                if (feature.properties.DP) {
                    SinalidAPI.listMissingPeople(onUpdateSinalidData, feature.properties.id)
                }
            })
        }
    }
    const onUpdateSinalidData = data => {
        dispatch(sinalidData(data))
    }
    const onDrawUpdateWithPolygonData = data => {
        dispatch(populateStateWithPolygonData(data))
    }
    return {
        /**
         * Create URL to get layers data and populate data table
         * @param e - Event bubbled on map click
         * @param layers - Active Layers array
         */
        handleMapClick: (e, layers, toolbarActive, googleApiToken) => {
            // update last click data
            const map = e.target
            const clickData = {
                BBOX: map.getBounds().toBBoxString(),
                WIDTH: map.getSize().x,
                HEIGHT: map.getSize().y,
                X: ~~map.layerPointToContainerPoint(e.layerPoint).x,
                Y: ~~map.layerPointToContainerPoint(e.layerPoint).y
            }
            dispatch(updateLastClickData(clickData))

            // fetch layer data for clicked point if needed
            let urls = layers.map(l => {
                return GeoAPI.createUrl({
                    layer: l,
                    clickData,
                    featureCount: MAX_ITEMS_TO_LOAD,
                })
            })
            GeoAPI.getLayersData(onUpdateWithSelectedLayerData, urls)

            // open street view data if needed
            if (toolbarActive === 'streetView') {
                window.open(`https://www.google.com/maps/embed/v1/streetview?key=${googleApiToken}&location=${e.latlng.lat},${e.latlng.lng}`, '_blank')
            }
        },
        onUpdateBasemapLoadingStatus: () => {
            dispatch(updateBasemapLoadingStatus())
        },
        handleMapMove: e => {
            const map = e.target
            const mapBounds = map.getBounds()
            const mapCenter = map.getCenter()
            const mapZoom = map.getZoom()
            const mapData = {
                bounds: mapBounds,
                lat: mapCenter.lat,
                lng: mapCenter.lng,
                zoom: mapZoom,
            }
            dispatch(lastMapPosition(mapData))
        },
        onDraw: (e, coordinates, activeLayers) => {
            const map = e.target
            dispatch(startPolygonDataRequest())
            GeoAPI.getPolygonData(onDrawUpdateWithPolygonData, coordinates, activeLayers)
        },
        onPolygonDelete: () => {
            dispatch(removePolygonData())
        },
    }
}

const LeafletMapContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(LeafletMap)

export default LeafletMapContainer


<|start_filename|>src/components/Api/ScaAPI.js<|end_filename|>
import axios from 'axios'

const ScaAPI = {
    /**
    * Call sca api and log user on system
    * @param callback function to call when process is completed
    */
    logInUser(callback, username, password) {
        axios
            .post(`/mpmapas/api/authentication?password=${password}&username=${username}`)
            .then((response) => {
                callback(response)
            })
            .catch((error) => {
                callback(error.response)
            })
    },
    logOutUser(callback) {
        axios
            .get(`/mpmapas/api/logout`)
            .then((response) => {
                console.log('Logout response',response);
            })
            .catch((error) => {
                console.log('Logout error',error);
            })
    },
    authenticate(callback) {
        axios
            .get(`/mpmapas/api/authenticate`)
            .then((response) => {
                callback(response)
            })
            .catch( (error) => {
                callback(error.response)
            })
    }
}

export default ScaAPI


<|start_filename|>src/components/ExportList/ExportList.js<|end_filename|>
import React from 'react'
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'

const ExportList = ({layers, mapProperties, onDownloadClick, onDownloadEnd}) => {

    /**
     * Opacity fixes that needs to be done before html2canvas() call
     */
    function html2canvasBefore() {
        const mapLayers = document.querySelectorAll(".leaflet-tile-container")
        mapLayers.forEach(mapLayer => {
            let fec = mapLayer.firstElementChild
            let src = fec.getAttribute('src')
            if (src && src.includes('retangulo')) {
                mapLayer.parentElement.style.opacity = 1
                mapLayer.children.forEach(mapLayerChildren => mapLayerChildren.style.opacity = 0.5)
            }
        })
    }

    /**
     * Opacity fixes that needs to be done after html2canvas() call
     */
    function html2canvasAfter() {
        const mapLayers = document.querySelectorAll(".leaflet-tile-container")
        mapLayers.forEach(mapLayer => {
            let fec = mapLayer.firstElementChild
            let src = fec.getAttribute('src')
            if (src && src.includes('retangulo')) {
                mapLayer.parentElement.style.opacity = 0.5
                mapLayer.children.forEach(mapLayerChildren => mapLayerChildren.style.opacity = 1)
            }
        })
    }

    /**
     * Call download of given URL with specified filename
     * @param {string} url - The URL to be downloaded
     * @param {string} filename - The name of the file to be downloaded
     */
    function callDownload(url, filename) {
        let link = document.createElement('a')
        link.setAttribute('href', url)
        link.setAttribute('download', filename)
        // On Firefox .click() require node to be on DOM tree
        document.body.appendChild(link)
        link.click()
        link.parentNode.removeChild(link)
        endLoader()
    }

    /**
     *
     * @param {object} layers - The active layers
     * @param {string} format - The format used to export data from Geoserver
     */
    function exportMapData(layers, mapProperties, format) {
        let selectedLayers = layers.filter(layer => layer.selected)
        if(selectedLayers.length === 0){
            endLoader()
        }

        selectedLayers.forEach(layer => {
            // get area visible on the screen
            let CQL_FILTER = `BBOX(geom,${mapProperties.currentCoordinates.bounds._southWest.lng},${mapProperties.currentCoordinates.bounds._southWest.lat},${mapProperties.currentCoordinates.bounds._northEast.lng},${mapProperties.currentCoordinates.bounds._northEast.lat},'EPSG:4326')`

            // if an area was selected on Global Filter
            if (mapProperties.placeToCenter && mapProperties.placeToCenter.tipo !== 'ESTADO') {
                let layerCQLFilterParameter = 'cod_' + mapProperties.placeToCenter.tipo.toLowerCase()
                if (mapProperties.placeToCenter.tipo === 'MUNICIPIO') {
                    layerCQLFilterParameter = 'cod_mun'
                }
                let geom = `'tipo=''${mapProperties.placeToCenter.tipo}'' and ${layerCQLFilterParameter}=''${mapProperties.placeToCenter['cd_'+mapProperties.placeToCenter.tipo.toLowerCase()]}''`

                // use it instead
                CQL_FILTER = "INTERSECTS(geom, querySingle('plataforma:busca_regiao', 'geom'," + geom + "'))"
            }

            // if the layer is filtered
            if (layer.filterKey && layer.filterValue) {
                CQL_FILTER += `AND strToLowerCase(${layer.filterKey}) LIKE '%25${layer.filterValue}%25'`
            }

            let url = `http://apps.mprj.mp.br/geoserver/plataforma/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${layer.layerName}&SRSNAME=EPSG:4326&outputFormat=${format}&CQL_FILTER=${CQL_FILTER}`
            let filename = `${layer.name}.${format === "excel2007" ? "xlsx" : format}`

            // shapefile download breaks with charset
            if (format !== 'SHAPE-ZIP') {
                url += '&format_options=CHARSET:UTF-8'
            }

            callDownload(url, filename)

            // show charset alert for shapefile
            if (format === 'SHAPE-ZIP') {
                alert('Ao abrir o shapefile selecione o charset "ISO-8859-1".')
            }
        })
    }

    /**
     * The function exports the map as a image using the lib HTML2Canvas.
     * It captures the document body, put on a canvas element and downloads as a jpg image file.
     */
    function exportMapImage() {
        html2canvasBefore()
        html2canvas(document.body, {
            useCORS: true, // CORS must be active to render the base map on canvas element
			onrendered: function(canvas) {
                let url = canvas.toDataURL('image/png')
                callDownload(url, 'mp_em_mapas.png')
		  	},
        })
        html2canvasAfter()
    }

    /**
     * The function exports the map as a image using the lib HTML2Canvas.
     * It captures the document body, put on a canvas element and downloads as a jpg image file.
     */
    function exportMapPDF() {
        html2canvasBefore()
        html2canvas(document.body, {
            useCORS: true, // CORS must be active to render the base map on canvas element
			onrendered: function(canvas) {
                let imgData = canvas.toDataURL('image/png')
                let doc = new jsPDF('l', 'mm', 'a4')

                // Scale the canvas image of the application to an A4 landscape size
                doc.addImage(imgData, 'PNG', 0, 0, 297, 210)
                doc.save('mp_em_mapas.pdf')
                endLoader()
		  	},
        })
        html2canvasAfter()
    }

    /**
     * The function initializes a loader for download data.
     */
    function initializeLoader() {
        onDownloadClick()
    }

    /**
     * The function ends the loader for download data.
     */
    function endLoader() {
        onDownloadEnd()
    }

    return (
        <ul className="export-list">
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapImage()
                    }
                }>Imagem (png)</a>
            </li>
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapPDF()
                    }
                }>Documento (pdf)</a>
            </li>
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapData(layers, mapProperties, "csv")
                    }
                }>Planilha (csv)</a>
            </li>
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapData(layers, mapProperties, "excel2007")
                    }
                }>Planilha (xlsx)</a>
            </li>
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapData(layers, mapProperties, "kml")
                    }
                }>Google Earth (kml)</a>
            </li>
            <li>
                <a className="export-list--link" role="button" onClick={
                    () => {
                        initializeLoader()
                        exportMapData(layers, mapProperties, "SHAPE-ZIP")
                    }
                }>Shape File (shp)</a>
            </li>
        </ul>
    )
}

export default ExportList


<|start_filename|>src/components/PolygonData/PolygonData.js<|end_filename|>
import React from 'react'
import Charts from '../Charts/Charts.js'

const PolygonData = ({ polygonData }) => {

    let populacao = polygonData.filter(l => l.category === "População")[0]

    let columns = []
    let keys = Object.keys(populacao.piramide_total)

    for (let i = 0; i < keys.length; i++) {
        let key = keys[i];
        let splittedKey = key.split('_')
        columns.push(
            [splittedKey[1]+'-'+splittedKey[2], key]
        )

    }

    let chartObject = {
        "charts": [
            {
                columns,
                "entity": "População",
                "title": "Pirâmide Etária",
                "type": "piramide"
            }
        ],
        "features": [
            {
                "properties": Object.assign({}, populacao.piramide_total)
            }
        ],
    }

    return (
        <div className="layer-item selected module-polygon-data">
            <div className="layer-item-header">
                <h2 className="layer-item-header--title">
                    Dados do polígono
                </h2>
            </div>
            <div className="layer-item-body">
                <table className="data-table">
                    <tbody>
                        {
                            polygonData.map( (l, index) => {
                                let title = l.category
                                let value = l.items.length
                                if(l.category === "População"){
                                    value = l.populacao_total
                                }
                                return (
                                    <tr key={index}>
                                        <td className="layer-item-more-info--title">{title}</td>
                                        <td className="layer-item-more-info--style-title">{value}</td>
                                    </tr>
                                )
                            })
                        }
                    </tbody>
                </table>
                <Charts layer={chartObject}/>
            </div>
        </div>
    )
}

export default PolygonData


<|start_filename|>src/components/LayerSubtitle/LayerSubtitleSpace.js<|end_filename|>
import React from 'react'
import LayerSubtitle from '../LayerSubtitle/LayerSubtitle.js'
import { DropTarget } from 'react-dnd'

const subtitleTarget = {
    drop(props, monitor) {
        return {target: props.layer}
    }
}

function collect(connect, monitor) {
    return {
        connectDropTarget: connect.dropTarget(),
        isOver: monitor.isOver()
    }
}

const LayerSubtitleSpace = ({
    connectDropTarget,
    id,
    isOver,
    lastClickData,
    layer,
    onClearLayerFilter,
    onIconMouseOut,
    onIconMouseOver,
    onLayerClick,
    onLayerDown,
    onLayerDrop,
    onLayerFilterSearch,
    onLayerRemove,
    onLayerUp,
    onLoadParams,
    onOpenLayerFilterModal,
    onOpenModal,
}) => {
    function over() {
        if (isOver){
            return {
                zIndex: 1,
                opacity: 0.5,
                backgroundColor: 'yellow',
            }
        }
        return {}
    }

    return connectDropTarget(
        <div
            className="layer-item-space"
            key={id}
            style={over()}
        >
            <LayerSubtitle
                layer={layer}
                key={id}
                lastClickData={lastClickData}
                onClearLayerFilter={onClearLayerFilter}
                onLayerClick={onLayerClick}
                onLayerUp={onLayerUp}
                onLayerDown={onLayerDown}
                onLayerDrop={onLayerDrop}
                onLayerFilterSearch={onLayerFilterSearch}
                onLayerRemove={onLayerRemove}
                onOpenLayerFilterModal={onOpenLayerFilterModal}
                onOpenModal={onOpenModal}
                onIconMouseOver={onIconMouseOver}
                onIconMouseOut={onIconMouseOut}
                onLoadParams={onLoadParams}
            />
        </div>
    )
}

export default DropTarget("layerSubtitle", subtitleTarget, collect)(LayerSubtitleSpace)


<|start_filename|>src/components/Modal/ModalContainer.js<|end_filename|>
import React from 'react'
import Modal from './Modal'
import { connect } from 'react-redux'
import {
    changeActiveTab,
    closeModal,
    getModalData,
    layerFilterLoaded,
    layerFilterLoading,
    loginUser,
    paginate,
    populateApp,
} from '../../actions/actions.js'
import GeoAPI from '../Api/GeoAPI.js'
import ScaAPI from '../Api/ScaAPI.js'

const mapStateToProps = state => {
    return {
        isFilterEmptyResult: state.isFilterEmptyResult,
        isLoadingFilter: state.isLoadingFilter,
        lastClickData: state.lastClickData,
        layers: state.layers,
        loginStatus: state.loginStatus,
        loginError: state.loginError,
        modalLayerFilterName: state.modalLayerFilterName,
        newsModal: state.newsModal,
        showAbout: state.showAbout,
        showLayerFilterModal: state.showLayerFilterModal,
        showLogin: state.showLogin,
        showModal: state.showModal,
    }
}

const mapDispatchToProps = dispatch => {
    const onAjaxDataFetched = layerData => {
        dispatch(getModalData(layerData))
    }

    const onGetModalData = (layer, lastClickData) => {
        const MAX_ITEMS_TO_LOAD = 9999

        let url = GeoAPI.createUrl({
            layer: layer,
            clickData: lastClickData,
            featureCount: MAX_ITEMS_TO_LOAD
        })
        GeoAPI.getLayerData(onAjaxDataFetched, url)
    }

    const populateCallback = xmlData => {
        dispatch(populateApp(xmlData, location.hash))
    }

    const loginCallback = data => {
        dispatch(loginUser(data))
        GeoAPI.getContent(populateCallback)
    }

    const authenticate = ({username, password}) => {
        ScaAPI.logInUser(loginCallback, username, password)
    }

    const authenticateLogout = () => {
        ScaAPI.logOutUser(loginCallback)
    }

    const onLayerFilterSearchLoaded = data => {
        dispatch(layerFilterLoaded(data))
    }

    return {
        /**
         * Fetch data from server to get content
         * of the selected tab
         */
        onChangeActiveTab: (layer, lastClickData) => {
            dispatch(changeActiveTab(layer))
            var selectedLayer = layer
            // Call AJAX
            onGetModalData(selectedLayer, lastClickData)
        },
        onCloseModal: () => {
            dispatch(closeModal())
        },
        onLayerFilterSearch: (layerName, parameterKey, parameterValue) => {
            dispatch(layerFilterLoading(layerName, parameterKey, parameterValue))
            GeoAPI.getLayerFilteredData(layerName, parameterKey, parameterValue, onLayerFilterSearchLoaded)
        },
        onLoginClick: data => {
            authenticate(data)
        },
        onPaginate: (layer, page) => {
            dispatch(paginate(layer, page))
        },
    }
}

const ModalContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Modal)

export default ModalContainer


<|start_filename|>src/components/Modal/Modal.js<|end_filename|>
import React from 'react'
import About from './Contents/About.js'
import LayerFilter from './Contents/LayerFilter.js'
import Login from './Contents/Login.js'
import News from './Contents/News.js'
import Table from './Contents/Table.js'

const Modal = ({
    isFilterEmptyResult,
    isLoadingFilter,
    lastClickData,
    layers,
    loginError,
    loginStatus,
    modalLayerFilterName,
    newsModal,
    showAbout,
    showLayerFilterModal,
    showLogin,
    showModal,
    onChangeActiveTab,
    onCloseModal,
    onGetModalData,
    onLayerFilterSearch,
    onLoginClick,
    onPaginate,
}) => {

    function handleCloseModal() {
        return onCloseModal()
    }

    let ContentComponent =
        <Table
            layers={layers}
            onGetModalData={onGetModalData}
            onChangeActiveTab={onChangeActiveTab}
            onPaginate={onPaginate}
            onCloseModal={handleCloseModal}
        />
    let sectionClassName = "modal"
    let modalTitle = "Tabela de Registros"

    if (!showModal) {
        return null
    }

    if (newsModal) {
        newsModal = false
        sectionClassName = "news-modal"
        modalTitle = "Últimas atualizações e novidades"

        ContentComponent = <News onCloseModal={handleCloseModal}/>
    }

    if (showLogin) {
        sectionClassName = "login-modal"
        modalTitle = "Login"

        ContentComponent = <Login onLoginClick={onLoginClick} loginError={loginError} loginStatus={loginStatus}/>
    }

    if (showAbout) {
        sectionClassName = "about-modal"
        modalTitle = "Sobre"
        ContentComponent = <About />
    }

    if (showLayerFilterModal) {
        sectionClassName = "filter-modal"
        modalTitle = "Filtro de camada"
        ContentComponent = <LayerFilter
            isFilterEmptyResult={isFilterEmptyResult}
            isLoadingFilter={isLoadingFilter}
            layers={layers}
            modalLayerFilterName={modalLayerFilterName}
            onLayerFilterSearch={onLayerFilterSearch}
        />
    }

    return (
        <div className="modal-container">
            <section className={sectionClassName}>
                <h1 className="modal--title">
                    {modalTitle}
                    <span className="modal--close-button" onClick={handleCloseModal}></span>
                </h1>
                {ContentComponent}
            </section>
        </div>
    )
}

export default Modal


<|start_filename|>src/components/Modal/Contents/Login.js<|end_filename|>
import React from 'react'

const Login = ({ loginError, loginStatus, onLoginClick }) => {
    let userNameInput;
    let passwordInput;

    const handleKeyPress = (target) => {
        if(target.charCode === 13){
            console.log('Enter clicked!!!')
            let data = {
                "username": userNameInput.value,
                "password": btoa(<PASSWORD>.<PASSWORD>),
            }
            onLoginClick(data)
        }
    }
    const handleLoginClick = () => {
        let data = {
            "username": userNameInput.value,
            "password": btoa(<PASSWORD>),
        }
        onLoginClick(data)
    }

    return (
        <article className="login-container">
            <form>
                <fieldset>
                    <span>
                        <label> Nome de usuário </label>
                        <input type="text" onKeyPress={handleKeyPress} ref={(input) => { userNameInput = input; }} />
                    </span>
                    <span>
                        <label> Senha </label>
                        <input type="password" onKeyPress={handleKeyPress} ref={(input) => { passwordInput = input; }}/>
                    </span>
                    <a role="button" className="login-button" onClick={() => handleLoginClick()}>Entrar</a>
                    {loginError ? <span className="error"> Credenciais erradas. Tente novamente. </span> : null}
                </fieldset>
            </form>
        </article>
    )
}

export default Login


<|start_filename|>webpack.config.js<|end_filename|>
const webpack = require('webpack');
const path = require('path');

const sourcePath = path.join(__dirname, './src');
const staticsPath = path.join(__dirname, './static');

var ExtractTextPlugin = require('extract-text-webpack-plugin');

var apiHost, workspace, geoServerURL, initialMapCoordinates, highlightLayer;

var sinalidURL = 'http://apps.mprj.mp.br/sinalid';

var setupAPI = function () {
    // eventually this could be different for `process.env.NODE_ENV`, but for now it will be the same
    // those double quotes look weird, but they are replaced as is, so they need to be quoted.
    workspace = "'plataforma'";
    apiHost = "'/geoserver/plataforma/wms'";
    if (process.env.NODE_ENV === 'mprj') {
        geoServerURL = 'http://p-mapas02:8080/geoserver';
    } else {
        geoServerURL = 'http://apps.mprj.mp.br/geoserver';
    }
}
setupAPI();

module.exports = function (env) {
    const nodeEnv = env && env.prod ? 'production' : 'development';
    const isProd = nodeEnv === 'production';

    const plugins = [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: Infinity,
            filename: 'vendor.bundle.js'
        }),
        new webpack.EnvironmentPlugin({
            NODE_ENV: nodeEnv,
        }),
        new webpack.NamedModulesPlugin(),
        new ExtractTextPlugin('./styles.css'),
        new webpack.DefinePlugin({
            __API__: apiHost,
            __WORKSPACE__: workspace,
            __INITIAL_MAP_COORDINATES__: {
                lat: -22.25,
                lng: -42.5,
                zoom: 8,
            }
        }),
    ];

    var sassUse;

    if (isProd) {
        plugins.push(
            new webpack.LoaderOptionsPlugin({
                minimize: true,
                debug: false
            }),
            new webpack.optimize.UglifyJsPlugin({
                compress: {
                    warnings: false,
                    screw_ie8: true,
                    conditionals: true,
                    unused: true,
                    comparisons: true,
                    sequences: true,
                    dead_code: true,
                    evaluate: true,
                    if_return: true,
                    join_vars: true,
                },
                output: {
                    comments: false,
                },
            })
        );

        sassUse = ExtractTextPlugin.extract({
            fallback: 'style-loader', // The backup style loader
            use: 'css-loader!sass-loader'
        });
    } else {
        plugins.push(
            new webpack.HotModuleReplacementPlugin()
        );

        sassUse = [
            'style-loader',
            'css-loader',
            'sass-loader'
        ];
    }

    return {
        devtool: isProd ? 'source-map' : 'eval',
        context: sourcePath,
        entry: {
            js: 'components/App/App.js',
            vendor: ['react']
        },
        output: {
            path: staticsPath,
            filename: '[name].bundle.js',
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    loader: 'style-loader/url!file-loader'
                },
                {
                    test: /\.html$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'file-loader',
                        query: {
                            name: '[name].[ext]'
                        },
                    },
                },
                {
                    test: /\.(scss)$/,
                    use: sassUse
                },
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    use: [
                        'babel-loader'
                    ],
                },
                {
                    test: /\.png$/,
                    loader: 'url-loader',
                    query: { mimetype: 'image/png' }
                },
                {
                    test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
                    loader: "url-loader?limit=10000&mimetype=application/font-woff"
                },
                {
                    test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
                    loader: "url-loader?limit=10000&mimetype=application/font-woff"
                },
                {
                    test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                    loader: "url-loader?limit=10000&mimetype=application/octet-stream"
                },
                {
                    test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
                    loader: "file-loader"
                },
                {
                    test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                    loader: "url-loader?limit=10000&mimetype=image/svg+xml"
                }
            ],
        },
        resolve: {
            extensions: ['.webpack-loader.js', '.web-loader.js', '.loader.js', '.js', '.jsx'],
            modules: [
                path.resolve(__dirname, 'node_modules'),
                sourcePath
            ]
        },

        plugins,

        performance: isProd && {
            maxAssetSize: 100,
            maxEntrypointSize: 300,
            hints: 'warning',
        },

        stats: {
            colors: {
                green: '\u001b[32m',
            }
        },

        devServer: {
            contentBase: './public',
            historyApiFallback: true,
            port: 3000,
            compress: isProd,
            inline: !isProd,
            hot: !isProd,
            proxy: {
                '/mpmapas/*': {
                    target: 'http://apps.mprj.mp.br/mpmapas',
                    changeOrigin: true,
                    pathRewrite: {
                        '^/mpmapas': ''
                    }
                },
                '/geoserver/*': {
                    target: geoServerURL,
                    changeOrigin: true,
                    pathRewrite: {
                        '^/geoserver': ''
                    }
                },
                '/sinalid/*': {
                    target: sinalidURL,
                    changeOrigin: true,
                    pathRewrite: {
                        '^/sinalid': ''
                    }
                }
            },
            stats: {
                assets: true,
                children: false,
                chunks: false,
                hash: false,
                modules: false,
                publicPath: false,
                timings: true,
                version: false,
                warnings: true,
                colors: {
                    green: '\u001b[32m',
                }
            }
        }
    };
};


<|start_filename|>src/components/HeaderRight/HeaderRight.js<|end_filename|>
import React from 'react'

/**
 * @param {Object[]} layers - this is array of layers.
 *
 * This function loops through all layers in the state
 * and returns true if at leas one layer is selected
 *
 * @return {boolean} - returns true if at least one layer
 * is selected, and false if none is selected.
 */
const aLayerIsSelected = (layers) => {
    if(Array.isArray(layers)){
        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i]
            if (layer.selected){
                return true
            }
        }
        return false
    }
}
const HeaderRight = ({layers, onHeaderClick}) => {
    var cssClass = 'header-right'
    if(!aLayerIsSelected(layers)){
        cssClass += ' hidden'
    }
    return (
        <div className={cssClass}>
            <a className="header-right--menu-button fa fa-list-ul" role="button" onClick={onHeaderClick}></a>
        </div>
    )
}

export default HeaderRight


<|start_filename|>src/components/SidebarRight/SidebarRight.js<|end_filename|>
import React from 'react'
import LayerSubtitleSpace from '../LayerSubtitle/LayerSubtitleSpace.js'
import PolygonData from '../PolygonData/PolygonData.js'
import HTML5Backend from 'react-dnd-html5-backend'
import { DragDropContext } from 'react-dnd'

const SidebarRight = ({
    layers,
    showSidebarRight,
    onLayerClick,
    orderByLayerOrder,
    lastClickData,
    polygonData,
    onClearLayerFilter,
    onIconMouseOut,
    onIconMouseOver,
    onLayerDown,
    onLayerDrop,
    onLayerFilterSearch,
    onLayerRemove,
    onLayerUp,
    onLoadParams,
    onOpenLayerFilterModal,
    onOpenModal,
    onRemoveAllLayers,
    onSidebarRightHideClick,
}) => {
    if (!orderByLayerOrder) {
        orderByLayerOrder = () => { return layers }
    }

    // define base class of element
    var cssClass = 'sidebar-right allow-transition-sidebar-right'
    // if showSidebarRight is false, add class to hide the element
    if (!showSidebarRight) {
        cssClass += ' hide-sidebar-right'
    }
    return (
        <div className={cssClass}>
            <div className="layer-list">
                <h1 className="layer-list--title">Camadas em exibição</h1>
                <button
                    className="layer-list--remove-all-layers-button"
                    onClick={onRemoveAllLayers}
                    aria-label="Remover todas as camadas"
                >
                    <i className="fa fa-trash-o" aria-hidden="true"></i>
                </button>
                <a
                    className="layer-list--close-button fa fa-times"
                    onClick={onSidebarRightHideClick}
                    role="button"
                    aria-label="Fechar painel"
                ></a>
                <div className="layer-item-space-container">
                    {
                        polygonData
                        ?
                        <PolygonData polygonData={polygonData} />
                        :
                        null
                    }
                    {layers ?
                        orderByLayerOrder(layers).reverse().map((layer, index) => {
                            return (
                                <LayerSubtitleSpace
                                    layer={layer}
                                    key={index}
                                    lastClickData={lastClickData}
                                    onClearLayerFilter={onClearLayerFilter}
                                    onIconMouseOut={onIconMouseOut}
                                    onIconMouseOver={onIconMouseOver}
                                    onLayerClick={onLayerClick}
                                    onLayerDown={onLayerDown}
                                    onLayerDrop={onLayerDrop}
                                    onLayerFilterSearch={onLayerFilterSearch}
                                    onLayerRemove={onLayerRemove}
                                    onLayerUp={onLayerUp}
                                    onLoadParams={onLoadParams}
                                    onOpenLayerFilterModal={onOpenLayerFilterModal}
                                    onOpenModal={onOpenModal}
                                />
                            )
                        })
                    : ''}
                </div>
            </div>
        </div>
    )
}
// need to wrap the top most component of your application
// to make children draggable with DragDropContext (set up React DnD).
export default DragDropContext(HTML5Backend)(SidebarRight)


<|start_filename|>src/components/SidebarLeft/SidebarLeft.js<|end_filename|>
import React from 'react'
import MenuHeader from '../MenuHeader/MenuHeader.js'
import MenuContainer from '../Menu/MenuContainer.js'
import SearchLayer from '../SearchLayer/SearchLayer.js'
import { withContentRect } from 'react-measure'

const SidebarLeft = withContentRect(['bounds', 'client'])(({
    measureRef,
    measure,
    contentRect,
    onSearchClick,
    onKeyUpSearch,
    showMenu,
    onClickMenuHeader,
    onBtnCleanSearch,
    searchString,
}) => {
    var cssClass = 'sidebar-left allow-transition'
    if (!showMenu) {
        cssClass += ' hide-sidebar'
    }
    return (
            <div ref={measureRef} className={cssClass}>
                <MenuHeader onClickMenuHeader={onClickMenuHeader}/>
                <MenuContainer sidebarLeftWidth={contentRect.client.width} sidebarLeftHeight={contentRect.client.height}/>
                <SearchLayer onSearchClick={onSearchClick} onKeyUpSearch={onKeyUpSearch} onBtnCleanSearch={onBtnCleanSearch} searchString={searchString}/>
            </div>
    )
})

export default SidebarLeft


<|start_filename|>src/components/GlobalFilter/Place.js<|end_filename|>
import React from 'react'

const Place = ({place, onPlaceClick}) => {
    const handleItemClick = (e) => {
        onPlaceClick({ text: e.target.textContent, id: e.target.dataset.id })
        e.stopPropagation();
    }

    let className = 'place'
    let hasOpenChild = false
    let hasChild = false
    let isBeingSearched = false

    if (place.nodes && place.nodes.length > 0) {
        hasChild = true
        // test if at least one element has show property true
        for (let i=0, l=place.nodes.length; i<l; i++) {
            if (place.nodes[i].show) {
                hasOpenChild = true
            }
        }
    }

    if (place.tipo === 'ESTADO') {
        hasChild = true
        hasOpenChild = true
        if (place.search) {
            isBeingSearched = true
        }
    }

    if (hasChild) {
        className += ' has-children'

        if (!hasOpenChild && (place.tipo === 'CRAAI' || place.tipo === 'TUTELA') ) {
            className += ' has-no-open-children'
        }
    }

    if (hasOpenChild) {
        className += ' has-open-children'
    }

    if (isBeingSearched) {
        className += ' hide-empty'
    }

    if ( ( (place.tipo === 'CRAAI' || place.tipo === 'TUTELA') && place.show === undefined) || place.show === true ) {
        return (
            <div className={className} onClick={(e) => handleItemClick(e)}>
                <span data-id={place.id}>{place.title}</span>
                {place.nodes && place.nodes.map( p =>
                    <Place onPlaceClick={onPlaceClick} key={p.id} place={p}/>
                )}
            </div>
        )
    } else {
        return null
    }
}

export default Place


<|start_filename|>tests/SearchLayer/SearchLayer.test.js<|end_filename|>
import React from 'react';
import SearchLayer from '../../src/components/SearchLayer/SearchLayer.js';
import renderer from 'react-test-renderer';

it('SearchLayer component renders correctly with no data', () => {
    const component = shallow(<SearchLayer/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>src/components/Modal/Contents/Table.js<|end_filename|>
import React from 'react'
import DataTable from '../../DataTable/DataTable.js'

const Table = ({
    layers,
    onGetModalData,
    onChangeActiveTab,
    onPaginate,
    onCloseModal,
}) => {

    function handleGetModalData(layer, lastClickData) {
        return onGetModalData(layer, lastClickData)
    }

    function handleChangeActiveTab(layer) {
        return onChangeActiveTab(layer, lastClickData)
    }

    function handlePaginate(layer, page) {
        return onPaginate(layer, page)
    }

    /**
     * @param {Object} data Active layer object
     * This function create and download a custom CSV file with
     * the data of the active layer shown on modal window
     */
    function createCsv(data) {
        let csvContent = "data:text/csv;charset=utf-8,\uFEFF"
        let titleArray = []
        let contentArray = []
        let titleData = ""
        let csvData = ""
        let link
        let encodeUri

        // Get the title line from the first entry of the object
        titleArray = Object.keys(data.modal.pages[0][0].properties)
        for(let i = 0; i < titleArray.length; i++) {
            titleData += `${titleArray[i]};`
        }
        titleData += "\n"

        // Get the content lines from the objects
        data.modal.pages.forEach(page => {
            page.forEach(p => {
                contentArray = Object.values(p.properties)
                for(let i = 0; i < contentArray.length; i++) {
                    csvData += `${contentArray[i]};`
                }
                csvData += "\n"
            })
        })
        csvContent += titleData
        csvContent += csvData
        encodeUri = encodeURI(csvContent)

        // Fake a anchor tag and link to create a custom name for the file and delete it after use
        link = document.createElement('a');
        link.setAttribute('href', encodeUri);
        link.setAttribute('download', "dados_mapa.csv");
        link.click();
    }

    const selectedLayers = layers.filter(l => l.selected)
    let selectedLayer

    return (
        <div className="table-container">
            <ul className="modal-layer-list">
                {
                    selectedLayers.map((layer, index) => {
                        let className = "modal-layer-list--link"
                        if (layer && layer.modal && layer.modal.activeLayer) {
                            className += ' active'
                            selectedLayer = layer
                        }

                        return (
                            <li className="modal-layer-list--item" key={index}>
                                <a role="button" download="dados_tabela.csv" className={className} onClick={() => handleChangeActiveTab(layer)}>
                                    {layer.title}
                                </a>
                            </li>
                        )
                    })
                }
            </ul>
            {selectedLayer && selectedLayer.modal && selectedLayer.modal.pages ?
                <DataTable layer={selectedLayer} isCollapsed={false} handlePaginate={handlePaginate}/>
                : ''
            }
            <ul className="modal-options">
                <li className="modal-options--export">
                    <button className="modal-options--link">
                        salvar
                        <span className="modal-options--icon fa fa-chevron-down"></span>
                    </button>
                    <ul className="modal-export-list">
                        <li>
                            {
                                selectedLayers.map((layer, index) => {
                                    if (layer.modal.activeLayer) {
                                        selectedLayer = layer

                                        return (
                                            <a key={index} role="button" className="modal-export-list--link" onClick={() => createCsv(selectedLayer)}>
                                                Planilha
                                                <span className="modal-export-list--extension">(csv)</span>
                                            </a>
                                        )
                                    }
                                })
                            }
                        </li>
                    </ul>
                </li>
                <li className="modal-options--back">
                    <button className="modal-options--link" onClick={onCloseModal}>
                        voltar
                    </button>
                </li>
            </ul>
        </div>
    )
}

export default Table


<|start_filename|>tests/MenuHeader/MenuHeader.test.js<|end_filename|>
import React from 'react';
import MenuHeader from '../../src/components/MenuHeader/MenuHeader.js';

it('MenuHeader component renders correctly with no data', () => {
    const component = shallow(<MenuHeader/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>src/components/Loading/LoadingContainer.js<|end_filename|>
import React from 'react'
import { connect } from 'react-redux'
import Loading from './Loading'

const mapStateToProps = (state, ownProps) => {
    return {
        layers: state.layers,
        showLoader: state.showLoader,
        downloadLoader: state.downloadLoader,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {}
}

const LoadingContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Loading)

export default LoadingContainer


<|start_filename|>src/components/App/App.js<|end_filename|>
import React from 'react'
import ReactDOM from 'react-dom'
import ReactGA from 'react-ga'
import logger from 'redux-logger'
import { applyMiddleware, createStore } from 'redux'
import { Provider } from 'react-redux'
import { populateApp } from '../../actions/actions.js'
import { checkUserLoggedIn } from '../../actions/actions.js'
import appReducer from './appReducer.js'
import GeoAPI from '../Api/GeoAPI.js'
import ScaAPI from '../Api/ScaAPI.js'
import HeaderContainer from '../Header/HeaderContainer.js'
import HeaderRightContainer from '../HeaderRight/HeaderRightContainer.js'
import HelpContainer from '../Help/HelpContainer.js'
import LeafletMapContainer from '../LeafletMap/LeafletMapContainer.js'
import LoadingContainer from '../Loading/LoadingContainer.js'
import ModalContainer from '../Modal/ModalContainer.js'
import SidebarLeftContainer from '../SidebarLeft/SidebarLeftContainer.js'
import SidebarRightContainer from '../SidebarRight/SidebarRightContainer.js'
import ToolbarContainer from '../Toolbar/ToolbarContainer.js'
import TooltipContainer from '../Tooltip/TooltipContainer.js'

require('./app.scss')
require('./polyfill.js')

ReactGA.initialize('UA-80844385-5');

function logPageView() {
    ReactGA.set({ page: window.location.pathname + window.location.search });
    ReactGA.pageview(window.location.pathname + window.location.search);
}

// start by removing pre-loading
document.getElementById('pre-loading').remove()

const store = createStore(appReducer, applyMiddleware(logger))

// check if user is logged in

const authenticateCallback = (data) => {
    store.dispatch(checkUserLoggedIn(data))
}

ScaAPI.authenticate(authenticateCallback)

const ajaxCallback = (xmlData) => {
    store.dispatch(populateApp(xmlData, location.hash))
}
GeoAPI.getContent(ajaxCallback)

const placesCallback = (xmlData) => {
    store.dispatch(populatePlaces(xmlData))
}

const orderByLayerOrder = (layers) => {
    return layers.sort(function(a, b) {
        return a.order - b.order
    })
}

// Toolbars (order is RTL)
const platformItems = [
    {
        name: 'about',
        tooltip: 'Sobre',
        className: 'fa fa-info about',
    },
    {
        name: 'download',
        tooltip: 'Baixar',
        className: 'fa fa-download download',
    },
    {
        name: 'share',
        tooltip: 'Compartilhar',
        className: 'fa fa-share-alt share',
    },
    {
        name: 'draw',
        tooltip: 'Desenhar',
        className: 'fa fa-pencil draw',
    },
    {
        name: 'polygonRequest',
        tooltip: 'Busca por desenho',
        className: 'fa fa-pencil-square-o polygonRequest',
    },
    {
        name: 'search',
        tooltip: 'Filtro por área ou órgão',
        className: 'fa fa-search search',
    },
    {
        name: 'login',
        tooltip: 'Login',
        className: 'fa fa-sign-in login login-logout',
    },
    {
        name: 'help',
        tooltip: 'Ajuda',
        className: 'fa fa-question help',
    },
]
const mapItems = [
    {
        name: 'basemaps',
        tooltip: 'Camadas de fundo',
        className: 'fa fa-map basemaps',
    },
    {
        name: 'searchStreet',
        tooltip: 'Busca de ruas/pontos de interesse',
        className: 'fa fa-binoculars searchStreet',
    },
    {
        name: 'streetView',
        tooltip: 'Street View™',
        className: 'fa fa-street-view streetView',
    },
]

const App = () => {
    return (
         <Provider store={store}>
            <div className="module-app">
                <LoadingContainer/>
                <HeaderContainer/>
                <HeaderRightContainer/>
                <HelpContainer/>
                <TooltipContainer/>
                <SidebarLeftContainer/>
                <SidebarRightContainer orderByLayerOrder={orderByLayerOrder}/>
                <LeafletMapContainer orderByLayerOrder={orderByLayerOrder}/>
                <ToolbarContainer orderByLayerOrder={orderByLayerOrder} items={platformItems} type="platform"/>
                <ToolbarContainer items={mapItems} type="map"/>
                <ModalContainer />
            </div>
        </Provider>
    )
}

ReactDOM.render(
    <App/>, document.getElementById('app')
)

logPageView()


<|start_filename|>esdoc.json<|end_filename|>
{
  "source": "src",
  "destination": "public/esdoc",
  "experimentalProposal": {
      "objectRestSpread": true
  }
}


<|start_filename|>src/components/Menu/MenuContainer.js<|end_filename|>
import React from 'react'
import Menu from './Menu'
import { connect } from 'react-redux'
import { toggleLayer, toggleMenu, untoggleAll, showDescription, hideDescription, updateScrollTop } from '../../actions/actions.js'

const getVisibleMenuElements = (menuItems) => {
    let hasOneVisibleMenuItem = false
    for (let menuItem of menuItems) {
        if (menuItem.selected) {
            hasOneVisibleMenuItem = true
        }
    }

    if (!hasOneVisibleMenuItem) {
        return menuItems
    }

    return menuItems.filter(menuItem => menuItem.selected)
}

const getVisibleLayers = (layers) => {
    let hasOneVisibleLayer = false
    for (let layer of layers) {
        if (layer.match) {
            hasOneVisibleLayer = true
        }
    }

    if (!hasOneVisibleLayer) {
        return layers
    }

    return layers.filter(layer => layer.match)
}

const mapStateToProps = (state, ownProps) => {
    return {
        menuItems: Array.isArray(state.menuItems) ? state.menuItems : [],
        layers: Array.isArray(state.layers) ? state.layers : [],
        currentLevel: state.currentLevel,
        sidebarLeftWidth: ownProps.sidebarLeftWidth,
        sidebarLeftHeight: ownProps.sidebarLeftHeight,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onLayerClick: (item) => {
            dispatch(toggleLayer(item))
        },
        onMenuItemClick: (item) => {
            dispatch(toggleMenu(item))
        },
        onUntoggleAllClick: () => {
            dispatch(untoggleAll())
        },
        onMouseOver: (e,
            layer,
            sidebarLeftWidth,
            parentHeight,
            top,
            bottom,
        ) => {
            if (layer) {
                dispatch(showDescription(
                    layer,
                    sidebarLeftWidth,
                    e.clientY,
                ))
            }
        },
        onMouseOut: (layer) => {
            if (layer) {
                dispatch(hideDescription(layer))
            }
        },
        onScroll: (scrollTop) => {
            dispatch(updateScrollTop(scrollTop))
        },
    };
};

const MenuContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Menu)

export default MenuContainer


<|start_filename|>src/components/App/reducers/geoServerXmlStyleReducer.js<|end_filename|>
// styles constants
const WORKSPACE = __WORKSPACE__
const ENDPOINT = __API__
const ICON_SIZE      = { x: 27,  y: 20  }
const THUMB_SIZE     = { x: 225, y: 150 }
const TOOLTIP_SIZE   = 750
const PROJECTION     = 'EPSG:3857'
const ICON_THUMB_URL = './resources/img/plataforma/icones/'

/**
 * Returns bounding box for a given layer
 * @param xmlNode XML node with a GeoServer layer
 * @return {String} bounding box string: minX,minY,maxX,maxY
 */
export const parseBoundingBox = (xmlNode) => {
    let boundingBox = xmlNode.getElementsByTagName('BoundingBox')[0]

    let minX = boundingBox.attributes.minx.value
    let minY = boundingBox.attributes.miny.value
    let maxX = boundingBox.attributes.maxx.value
    let maxY = boundingBox.attributes.maxy.value

    return `${minX},${minY},${maxX},${maxY}`
}

/**
 * Returns styles array
 * @param xmlNode XML node with a GeoServer layer
 * @param layer object with parsed layer data
 * @return {Object[]} Styles array
 */
export const parseStyle = (xmlNode, layer) => {
    let styles = []
    const styleCollection = xmlNode.getElementsByTagName('Style')

    for (let i=0, l=styleCollection.length; i<l; i++) {
        let style = styleCollection[i]

        let nameTags       = style.getElementsByTagName('Name')
        let titleTags      = style.getElementsByTagName('Title')
        let abstractTags   = style.getElementsByTagName('Abstract')
        let legendURLTags  = style.getElementsByTagName('LegendURL')

        let styleName      = nameTags.length      ? nameTags[0].textContent      : ''
        let styleTitle     = titleTags.length     ? titleTags[0].textContent     : ''
        let styleAbstract  = abstractTags.length  ? abstractTags[0].textContent  : ''
        let styleLegendURL = legendURLTags.length ? legendURLTags[0].textContent : ''

        let iconSize   = `&width=${ICON_SIZE.x}&height=${ICON_SIZE.y}`
        let thumbSize  = `&width=${THUMB_SIZE.x}&height=${THUMB_SIZE.y}`
        let iconURL    = `${ENDPOINT}?service=WMS&version=1.1.0&request=GetMap&bbox=${layer.bbox}${iconSize}&ssrs=${PROJECTION}&format=image%2Fpng&layers=${layer.layerName}&singleTile=true&styles=${styleName}`
        let thumbURL   =  iconURL.replace(iconSize, thumbSize)
        let iconExt    = `_${ICON_SIZE.x}_${ICON_SIZE.y}.png`
        let thumbExt   = `_${THUMB_SIZE.x}_${THUMB_SIZE.y}.png`
        let iconURL_2  =  ICON_THUMB_URL + layer.layerName.replace(':','_') + '-' + styleName.replace(':','_') + iconExt
        let thumbURL_2 =  iconURL_2.replace(iconExt, thumbExt)
        let tooltip    = `<span style="width:${TOOLTIP_SIZE}px;"><img src="${thumbURL_2}" onError="this.onerror=null;this.src='${thumbURL}'" width="${THUMB_SIZE.y}" class="estilo-tooltip"/><strong>${layer.title}</strong><br>${styleTitle}</span>`

        let styleObj = {
            'id'          : i,
            'title'       : styleTitle,
            'name'        : styleName,
            'description' : styleAbstract,
            'icon'        : iconURL,       // icon generated by GeoServer dynamically
            'icon_2'      : iconURL_2,     // cached icon
            'thumb'       : thumbURL,      // thumb generated by GeoServer dynamically
            'thumb_2'     : thumbURL_2,    // cached thumb
            'tooltip'     : tooltip,
        }

        styles.push(styleObj)
    }

    return styles
}


<|start_filename|>src/components/App/reducers/geoServerXmlReducer.js<|end_filename|>
import { parseBoundingBox, parseStyle } from './geoServerXmlStyleReducer'

const WORKSPACE = __WORKSPACE__
const ENDPOINT = __API__

/**
 * Parses XML response from GeoServer, creating a layers array
 * @param response response from GeoServer API (as XML)
 */
const geoServerXmlReducer = (response) => {
    let layers = []

    // adds iterators to XML nodes, so we can run forEach on them
    NodeList.prototype.forEach = Array.prototype.forEach
    HTMLCollection.prototype.forEach = Array.prototype.forEach

    // get root layers node, and parse layer data for each layer
    const parser = new DOMParser()
    const xmlDoc = parser.parseFromString(response.data, 'text/xml')
    xmlDoc.firstChild.childNodes.forEach((rootChildrenNode) => {
        if (rootChildrenNode.nodeName === 'Capability') {
            rootChildrenNode.childNodes.forEach( (capabilityChildrenNode) => {
                if (capabilityChildrenNode.nodeName === 'Layer') {
                    capabilityChildrenNode.childNodes.forEach((rootLayerChildrenNode) => {
                        if (rootLayerChildrenNode.nodeName === 'Layer') {
                            layers = parseLayerNode(rootLayerChildrenNode, layers)
                        }
                    })
                }
            })
        }
    })

    return layers
}

/**
* Parses XML node for a single layer, populating GeoAPI.layers array.
* @param xmlNode XML node with a GeoServer layer
* @param layers layers array we're building
*/
const parseLayerNode = (xmlNode, layers) => {
    if (isValidLayer(xmlNode)) {
        const charts = []
        const caops = []
        let menu = ''
        let menu2 = ''
        let name
        let title
        let abstract
        let table
        let stylesOrdered = false
        let RESTRICTED = false

        // gets name, title, abstract, and keywords for caops and menu
        xmlNode.childNodes.forEach(layerChildrenNode => {
            const nodeName = []
            nodeName.Name = () => { name = layerChildrenNode.textContent }
            nodeName.Title = () => { title = layerChildrenNode.textContent }
            nodeName.Abstract = () => { abstract = layerChildrenNode.textContent }
            nodeName.KeywordList = () => {
                layerChildrenNode.childNodes.forEach(keywordNode => {
                    if (keywordNode.nodeName === 'Keyword') {
                        const keywordsArray = keywordNode.textContent.split(':')
                        if (keywordsArray[0] === 'cao') {
                            caops.push(keywordsArray[1])
                        }
                        if (keywordsArray[0] === 'tabela') {
                            try {
                                table = JSON.parse(keywordsArray[1])
                            }
                            catch (e) {
                                // declarações para manipular quaisquer exceções
                                console.log(e)
                                table = []
                            }
                        }
                        if (keywordsArray[0] === 'menu') {
                            menu = keywordsArray[1]
                        }
                        if (keywordsArray[0] === 'menu2') {
                            const menu2Array = [
                                keywordsArray[1]
                            ]
                            if (keywordsArray.length > 2) {
                                // copy all submenus
                                for (let i = 2, l = keywordsArray.length; i < l; i++) {
                                    menu2Array.push(keywordsArray[i])
                                }
                            }
                            menu2 = menu2Array
                        }
                        if (keywordsArray[0] === 'grafico') {
                            const chartData = keywordsArray[1].split('|')
                            const chartColumns = chartData[3].split(',')
                            for (let c = 0, lc = chartColumns.length; c < lc; c++) {
                                chartColumns[c] = chartColumns[c].split('/')
                            }
                            const chartObject = {
                                type: chartData[0],
                                title: chartData[1],
                                entity: chartData[2],
                                columns: chartColumns
                            }
                            charts.push(chartObject)
                        }
                        if (keywordsArray[0] === 'ordenar') {
                            stylesOrdered = true
                        }
                        if (keywordsArray[0] === 'restrito') {
                            RESTRICTED = true
                        }
                    }
                })
            }

            if (nodeName[layerChildrenNode.nodeName]) {
                nodeName[layerChildrenNode.nodeName]()
            }
        })

        // create layer object
        var layer = {
            name,
            title,
            caops,
            menu,
            menu2,
            table,
            charts,
            stylesOrdered,
            id:         `${WORKSPACE}_${name}`,
            workspace:   WORKSPACE,
            display:     true,
            restricted:  RESTRICTED,
            layerName:  `${WORKSPACE}:${name}`,
            description: abstract,
            bbox:        parseBoundingBox(xmlNode),
            key:         layers.length,
        }

        // get layer styles
        layer.styles = parseStyle(xmlNode, layer)

        // add to layers array if they have a valid menu key (i.e. is published)
        if (menu) {
            layers.push(layer)
        }
    }

    return layers
}

/**
* Check if the given XML node represents a valid layer.
* The layers we use must have a KeywordList node, with at least one element inside.
* @param xmlNode XML node with a GeoServer layer
* @returns {Boolean}
*/
const isValidLayer = (xmlNode) => {
    let ret = false

    xmlNode.childNodes.forEach((layerChildrenNode) => {
        if (layerChildrenNode.nodeName === 'KeywordList' && layerChildrenNode.childNodes.length > 0) {
            if (ret === false) {
                ret = true
            }
        }
    })

    return ret
}

export default geoServerXmlReducer


<|start_filename|>copy_modules.js<|end_filename|>
var chalk = require('chalk')
var mkdirp = require('mkdirp')
var copydir = require('copy-dir')
var copyFile = require('cpx')

var folderFA = 'src/components/App/sass/font-awesome'
var folderLeaflet = 'src/components/App/sass/leaflet'
var folderLeafletImages = 'src/components/App/sass/leaflet/images'

console.log(chalk.blue('Copying Modules...'))

try {
    mkdirp(folderFA)
    copydir.sync('node_modules/font-awesome', folderFA)

    mkdirp(folderLeaflet)
    copyFile.copySync('node_modules/leaflet/dist/leaflet.css', folderLeaflet)

    mkdirp(folderLeafletImages)
    copydir.sync('node_modules/leaflet/dist/images', folderLeafletImages)

    console.log(chalk.green('Job done, moving on'))
} catch (e) {
    console.log(chalk.red('An error occured: \n' + e))
}


<|start_filename|>src/components/Menu/Menu.js<|end_filename|>
import React from 'react'
import MenuItem from './MenuItem'
import { withContentRect } from 'react-measure'

const Menu = withContentRect(['bounds', 'client', 'scroll'])(({
    measureRef,
    measure,
    contentRect,
    menuItems,
    menuTitle,
    parentMenuTitle,
    submenus,
    layers,
    onLayerClick,
    onMenuItemClick,
    onMouseOver,
    onScroll,
    sidebarLeftWidth,
    sidebarLeftHeight,
    sidebarLeftScrollTop,
    onMouseOut,
    onUntoggleAllClick,
    selected,
    currentLevel,
    allMenuItems,
    idMenu
}) => {
    /**
     * This function gets an unordered array of layers indexes and returns
     * an ordered array of indexes
     * @param {number[]} layersIndexSet - this is a subset of layers indexes.
     * @param {object[]} allLayers - this is an array containing all layers.
     * @return {number[]} ordered array of layers indexes
     */
    function orderLayersAlphabetically (layersIndexSet, allLayers) {
        var layersSet = []
        // check if it is an array of layers indexes
        if(layersIndexSet.length > 0 && typeof layersIndexSet[0] === 'number'){
            for (var i = 0; i < allLayers.length; i++) {
                for (var j = 0; j < layersIndexSet.length; j++) {
                    /*
                        find the object on allLayers array that
                        corresponds to each layer index on layersIndexSet
                        and then push them into layersSet array
                    */
                    if (i === layersIndexSet[j]) {
                        layersSet.push(allLayers[i])
                    }
                }
            }

            // order layersSet array by it's title property
            var layersSetOrdered = layersSet.sort(function(a, b){
                return (a.title > b.title) ? 1 : (a.title < b.title) ? -1 : 0
            })
            layersIndexSet = []

            /*
                for each item in the ordered array, get its index property key
                and push it into a brand new layersIndexSet
            */
            for (var k = 0; k < layersSetOrdered.length; k++) {
                if (layersSetOrdered[k]) {
                    layersIndexSet.push(layersSetOrdered[k].key)
                }
            }
        }
        return layersIndexSet
    }

    if(!allMenuItems){
        allMenuItems = menuItems
    }

    // add a selected class to the menu if it is selected
    let menuClassName = "menu menu-container" + (selected ? ' selected' : '')

    // check if there are items hidden by search
    let itemsNotMatched = false
    if(menuItems){
        for (let menuItem of menuItems) {
            if (menuItem.title && menuItem.match === false) {
                itemsNotMatched = true
            }
        }
    }

    // check if this menu has submenu children selected
    if (allMenuItems) {
        allMenuItems.forEach(oneMenuItem => {
            // first find this menu
            if (oneMenuItem.idMenu === idMenu) {
                // if it is selected
                if (oneMenuItem.selected) {
                    // check if any of this menu's submenu is selected
                    oneMenuItem.submenus.forEach(submenu => {
                        allMenuItems.forEach(thisMenuItem => {
                            if(thisMenuItem.idMenu === submenu && thisMenuItem.selected){
                                // if it is selected, add class to father's menu
                                menuClassName += ' has-submenu-opened'
                            }
                        })
                    })
                }
            }
        })
    }

    /**
     * Returns a JSX MenuItem call for a given item
     * @param {object} item menu item
     * @return {string} JSX markup for the component
     */
    function menu(item) {
        if(item.isSubMenu){
            return null
        }
        return (
            <MenuItem
                item={item}
                layers={layers}
                onLayerClick={onLayerClick}
                onMenuItemClick={onMenuItemClick}
                onItemClick={Number.isInteger(item) ? onLayerClick : onMenuItemClick}
                onMouseOver={onMouseOver}
                sidebarLeftWidth={sidebarLeftWidth}
                sidebarLeftHeight={sidebarLeftHeight}
                onMouseOut={onMouseOut}
                parentMenuTitle={menuTitle}
                currentLevel={currentLevel}
                allMenuItems={menuItems}
                key={Number.isInteger(item) ? item : item.idMenu}
            />
        )
    }

    /**
     * Returns a JSX MenuItem call for a given submenu
     * @param {object} submenu menu's submenu
     * @return {string} JSX markup for the component
     */
    function subMenu(submenu) {
        let thisMenu

        allMenuItems.forEach( (relativeItem) => {
            if (submenu === relativeItem.idMenu) {
                thisMenu = relativeItem
            }
        })

        return (
            <MenuItem
                item={thisMenu}
                layers={layers}
                onLayerClick={onLayerClick}
                onMenuItemClick={onMenuItemClick}
                onItemClick={Number.isInteger(thisMenu) ? onLayerClick : onMenuItemClick}
                onMouseOver={onMouseOver}
                sidebarLeftWidth={sidebarLeftWidth}
                sidebarLeftHeight={sidebarLeftHeight}
                onMouseOut={onMouseOut}
                parentMenuTitle={menuTitle}
                currentLevel={currentLevel}
                allMenuItems={allMenuItems}
                key={thisMenu.idMenu}
            />
        )
    }

    /**
     * Renders the component.
     *
     * @return {string} - HTML markup for the component
     */
    return (
        <ul ref={measureRef} className={menuClassName} onScroll={
            (e) => {
                if(window.myTimeout){
                    clearTimeout(window.myTimeout)
                }
                // creating timeout, so the scroll event does not fire every time
                window.myTimeout = setTimeout(() => {
                    measure() // make react-measure recalculate measures

                    // get menu container and it's scrollTop value
                    let menuContainer = document.getElementsByClassName('sidebar-left')[0].childNodes[1]
                    let currentScrollValue = menuContainer.scrollTop

                    // we need to make scrollTop align with the top of an item
                    // so we round the scrollTop value considering the 33 element height
                    const elementHeight = 33
                    let roundedScrollValue = Math.round( currentScrollValue / elementHeight ) * elementHeight

                    // reset menu container scrollTop value to the rounded value
                    menuContainer.scrollTop = roundedScrollValue

                    // if the roundedScroll value is higher than the maximum scrollTop value possible,
                    // the scrollTop will not change and our menu and tooltips will be desynchronized
                    var newScrollValue = menuContainer.scrollTop
                    var deltaScrollValue = roundedScrollValue - newScrollValue
                    // so we need to check if roundedScrollValue equals newScrollValue
                    if(deltaScrollValue !== 0){
                        // if is not equal, than we need to correct the height of menu container
                        var computedHeight = parseInt(window.getComputedStyle(menuContainer, null).height) // get the computed height of menu container
                        computedHeight += deltaScrollValue // add the difference between newScrollValue and roundedScrollValue
                        menuContainer.style.height = computedHeight.toString() + 'px' //set menu container's new corrected height
                    }
                    onScroll(roundedScrollValue)
                }, 100)
            }
        }>
            {
                (!menuTitle && currentLevel > 0 && !itemsNotMatched) ?
                    <li className="menu-item-all-layers" onClick={onUntoggleAllClick}>Todas as camadas</li>
                : ''
            }
            {
                (submenus && submenus.length > 0) ?
                    submenus.map(
                        (submenu) => subMenu(submenu)
                    )
                : ''
            }
            {
                menuItems ? orderLayersAlphabetically(menuItems, layers).map((item) => menu(item)) : ''
            }
        </ul>
    )
})

export default Menu


<|start_filename|>tests/Tooltip/Tooltip.test.js<|end_filename|>
import React from 'react';
import Tooltip from '../../src/components/Tooltip/Tooltip.js';
import renderer from 'react-test-renderer';

it('Tooltip component renders correctly with no data', () => {
    const component = shallow(<Tooltip/>);
    expect(component).toMatchSnapshot();
});

it('Tooltip component renders correctly with real data', () => {
    let tooltip = {
        "text":"Índice de Desenvolvimento da Educação Básica (IDEB) da rede estadual de educação. Leitura da tabela: I (Anos Iniciais), F (Anos Finais), E (Estadual), M (Municipal), P (Pública). Fonte: INEP Ano: 2015","show":true,"sidebarLeftWidth":300,"parentHeight":33,"top":230
    }

    const component = shallow(<Tooltip tooltip={tooltip}/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>tests/SidebarLeft/SidebarLeft.test.js<|end_filename|>
import React from 'react';
import SidebarLeft from '../../src/components/SidebarLeft/SidebarLeft.js';
import renderer from 'react-test-renderer';

it('SidebarLeft component renders correctly with no data', () => {
    const component = shallow(<SidebarLeft/>);
    expect(component).toMatchSnapshot();
});

it('SidebarLeft component renders correctly with real data -> menu open', () => {
    const component = shallow(<SidebarLeft showMenu={true}/>);
    expect(component).toMatchSnapshot();
});


<|start_filename|>tests/test_generator.js<|end_filename|>
var fs = require('fs');

var tests = [];

var test = {
    className: "",
    imports: ""
}

var dataConfig;

var dir = "/Users/rafael.tavares/projects-mp/github/inLoco-2.0/src/components";

function readConfigFile(){
    fs.readFile('/Users/rafael.tavares/projects-mp/github/inLoco-2.0/tests/tests-boilerplate.json', 'utf8', function (err,data) {
        if (err) {
            return console.log(err);
        }
        dataConfig = JSON.parse(data);
        dive(dir, dataConfig);
    });
}



var dive = function (dir, dataConfig) {
    fs.readdir(dir, function (err, list) {
        // Return the error if something went wrong
        if (err)
            return err;

        // For every file in the list
        list.forEach(function (file) {
            // Full path of that file
            var path = dir + "/" + file;
            var extension = file.split(".")[1];
            if(extension === "js"){
                read(path, dataConfig);
            }
            // Get the file's stats
            fs.stat(path, function (err, stat) {
                // If the file is a directory
                if (stat && stat.isDirectory())
                    // Dive into the directory
                    dive(path, dataConfig);
                // else
                    // Call the action
                    // action(null, path);
            });
        });
    });
};

function read(pathToFile, dataConfig){
    fs.readFile(pathToFile, 'utf8', function (err,data) {
        if (err) {
            return console.log(err);
        }
        data = data.split(" ");
        for (var index = 0; index < data.length; index++) {
            var element = data[index];
            if (element === "class"){
                var className = data[index+1];
                test = {
                    "className": "",
                    "imports": `import ${className} from "../../src/components/${className}/${className}.js"\n`
                };
                test.className = className;
                tests.push(test);
            }
        }
        //console.log(test);
        writeTest(test, dataConfig);
    });
}
readConfigFile();

function writeTest(test, dataConfig){
    for (var index = 0; index < dataConfig.imports.length; index++) {
            var what = dataConfig.imports[index].what;
            var where = dataConfig.imports[index].where;
            test.imports += "import "+ what + " from " + where + "\n";
        }
        for (var index = 0; index < dataConfig.tests.length; index++) {
            var element = dataConfig.tests[index];
            if (element.name === "snapshot"){
                var varName = test.className.charAt(0).toLowerCase() + test.className.slice(1);
                element.testString =
            `it('component renders correctly', () => {
    const ${varName} = shallow(<${test.className}/>);
    expect(${varName}).toMatchSnapshot();
})\n`;
                test.testString = element.testString;
            }
        }
        console.log(test);
        //writeTest(test);
    fs.writeFile(`/Users/rafael.tavares/projects-mp/github/inLoco-2.0/tests/auto/${test.className}.test.js`, test.imports + test.testString, { flag: "wx" }, function(err) {
        if(err) {
            return console.log(err);
        }
    });
}


<|start_filename|>src/components/ToolbarMenu/ToolbarMenu.js<|end_filename|>
import React from 'react'
import BaseMapList from '../BaseMapList/BaseMapList'
import ExportList from '../ExportList/ExportList'
import GlobalFilter from '../GlobalFilter/GlobalFilter'
import GooglePlacesContainer from '../GooglePlaces/GooglePlacesContainer'
import ShareUrl from '../ShareUrl/ShareUrl'

/**
 * Creates a toolbar menu (one for each toolbar item)
 * @param {object} p An object with parameters
 * @return {string} JSX string
 */
const ToolbarMenu = ({
    active,
    baseMaps,
    globalFilterType,
    item,
    mapProperties,
    layers,
    places,
    tutela,
    type,
    onChangeActiveBaseMap,
    onClearPlaceTutelaLayer,
    onContourChange,
    onGlobalFilterTypeChange,
    onKeyUpSearchPlaces,
    onKeyUpSearchTutela,
    onOpacityChange,
    onPlaceClick,
    onToolbarItemClick,
    onTutelaClick,
    onDownloadClick,
    onDownloadEnd,
    orderByLayerOrder,
}) => {
    let className = 'toolbar-menu'

    if (type === 'map') {
        className += ' map'
    }

    if (
        (!active || active !== item.name) ||
        item.name === 'draw' ||
        item.name === 'polygonRequest' ||
        item.name === 'help' ||
        item.name === 'about'
    ) {
        className += ' hidden'
    }

    return (
        <div className={className}>
            {
                item.name === 'download'
                ? <ExportList
                    layers={layers}
                    mapProperties={mapProperties}
                    onDownloadClick={onDownloadClick}
                    onDownloadEnd={onDownloadEnd}
                /> : null
            }
            {
                item.name === 'share'
                ? <ShareUrl
                    layers={layers}
                    mapProperties={mapProperties}
                    orderByLayerOrder={orderByLayerOrder}
                    onToolbarItemClick={onToolbarItemClick}
                /> : null
            }
            {
                item.name === 'basemaps'
                ? <BaseMapList
                    baseMaps={baseMaps}
                    onChangeActiveBaseMap={onChangeActiveBaseMap}
                /> : null
            }
            {
                item.name === 'search'
                ?   <GlobalFilter
                        globalFilterType={globalFilterType}
                        mapProperties={mapProperties}
                        places={places}
                        tutela={tutela}
                        onClearPlaceTutelaLayer={onClearPlaceTutelaLayer}
                        onContourChange={onContourChange}
                        onGlobalFilterTypeChange={onGlobalFilterTypeChange}
                        onKeyUpSearchPlaces={onKeyUpSearchPlaces}
                        onKeyUpSearchTutela={onKeyUpSearchTutela}
                        onOpacityChange={onOpacityChange}
                        onPlaceClick={onPlaceClick}
                        onTutelaClick={onTutelaClick}
                    />
                : ''
            }
            {
                item.name === 'streetView' ?
                    <span>clique no mapa para exibir o street view.</span>
                : null
            }
            {
                item.name === 'searchStreet' ?
                    <GooglePlacesContainer />
                : null
            }
        </div>
    )
}

export default ToolbarMenu


<|start_filename|>src/components/Help/HelpContainer.js<|end_filename|>
import React from 'react'
import Help from './Help'
import { connect } from 'react-redux'
import { hideHelp } from '../../actions/actions.js'

const mapStateToProps = (state, ownProps) => {
    return {
        showHelp: state.showHelp,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onIntrojsExit: () => {
            dispatch(hideHelp())
        }
    }
}

const HelpContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Help)

export default HelpContainer


<|start_filename|>src/components/Header/Header.js<|end_filename|>
import React from 'react'

const Header = ({onHeaderClick, showTooltipMenu}) => {
    let className = "menu-button--tootltip"
    if (!showTooltipMenu) {
        className += " hidden"
    }
    return (
        <div className="application-header">
            <div className="application-header--logo-placeholder">
            <a href="http://mpemmapas.mprj.mp.br/home.html" target="_blank">
                <img src={require('../../assets/img/logo.png')} alt="InLoco" className="application-header--logo"/>
            </a>
            </div>
            <a className="application-header--menu-button fa fa-bars focus-menu" role="button" onClick={onHeaderClick} data-html2canvas-ignore={true}>
                <span className={className}>Comece por aqui.</span>
            </a>
        </div>
    )
}

export default Header


<|start_filename|>src/actions/actions.js<|end_filename|>
// on App.js
export const populateApp = (xmlData, hash) => {
    return {
        type: 'POPULATE_APP',
        xmlData,
        hash,
    }
}

export const checkUserLoggedIn = (data) => {
    return {
        type: 'CHECK_LOGGED_IN_USER',
        data,
    }
}

// on HeaderContainer.js
export const showMenuLayer = () => {
    return {
        type: 'SHOW_MENU_LAYER',
    }
}

export const showSidebarRight = () => {
    return {
        type: 'SHOW_SIDEBAR_RIGHT',
    }
}

// on HelpContainer.js
export const hideHelp = () => {
    return {
        type: 'HIDE_HELP',
    }
}

// on LayerStylesCarousel.js
export const slideLeftStyles = item => {
    return {
        type: 'SLIDE_LEFT_STYLES',
        id: item.id,
    }
}

export const slideRightStyles = item => {
    return {
        type: 'SLIDE_RIGHT_STYLES',
        id: item.id,
    }
}

export const selectLayerStyle = (item, styleId) => {
    return {
        type: 'SELECT_LAYER_STYLE',
        id: item.id,
        styleId,
    }
}

//on LeafletMapContainer.js
export const populateStateWithLayerData = data => {
    return {
        type: 'POPULATE_STATE_WITH_LAYER_DATA',
        data,
    }
}

export const updateLastClickData = data => {
    return {
        type: 'UPDATE_LAST_CLICK_DATA',
        data,
    }
}

export const updateBasemapLoadingStatus = () => {
    return {
        type: 'UPDATE_BASEMAP_LOADING_STATUS',
    }
}

export const lastMapPosition = data => {
    return {
        type: 'LAST_MAP_POSITION',
        data,
    }
}

export const populateStateWithPolygonData = data => {
    return {
        type: 'POPULATE_STATE_WITH_POLYGON_DATA',
        data,
    }
}

export const showStreetView = data => {
    return {
        type: 'SHOW_STREET_VIEW',
        data,
    }
}

export const hideStreetView = () => {
    return {
        type: 'HIDE_STREET_VIEW',
    }
}

export const removePolygonData = () => {
    return {
        type: 'REMOVE_POLYGON_DATA'
    }
}

export const startPolygonDataRequest = () => {
    return {
        type: 'START_POLYGON_DATA_REQUEST'
    }
}

// on MenuContainer.js
export const toggleLayer = item => {
    return {
        type: 'TOGGLE_LAYER',
        id: item.id,
    }
}

export const toggleMenu = item => {
    return {
        type: 'TOGGLE_MENU',
        id: item.id,
        selected: item.selected,
    }
}

export const untoggleAll = () => {
    return {
        type: 'UNTOGGLE_MENUS',
    }
}

export const showDescription = (layer, sidebarLeftWidth, mouseY) => {
    return {
        type: 'SHOW_DESCRIPTION',
        id: layer.id,
        sidebarLeftWidth,
        mouseY,
    }
}

export const hideDescription = layer => {
    return {
        type: 'HIDE_DESCRIPTION',
        id: layer.id,
    }
}

export const updateScrollTop = scrollTop => {
    return {
        type: 'UPDATE_SCROLL_TOP',
        scrollTop,
    }
}

// on SidebarLeftContainer.js
export const closeToolbars = () => {
    return {
        type: 'CLOSE_TOOLBARS',
    }
}

export const searchLayer = text => {
    return {
        type: 'SEARCH_LAYER',
        text,
    }
}

export const cleanSearch = () => {
    return {
        type: 'CLEAN_SEARCH',
    }
}

export const hideMenuLayer = () => {
    return {
        type: 'HIDE_MENU_LAYER',
    }
}

// on SidebarRightContainer.js
export const hideSidebarRight = () => {
    return {
        type: 'HIDE_SIDEBAR_RIGHT',
    }
}

export const toggleLayerInformation = item => {
    return {
        type: 'TOGGLE_LAYER_INFORMATION',
        id: item.id,
    }
}

export const slideLayerUp = item => {
    return {
        type: 'SLIDE_LAYER_UP',
        id: item.id,
    }
}

export const slideLayerDown = item => {
    return {
        type: 'SLIDE_LAYER_DOWN',
        id: item.id,
    }
}

export const dropLayer = (dragged, target) => {
    return {
        type: 'DROP_LAYER',
        draggedPosition: dragged.order,
        targetPosition: target.order,
    }
}

export const removeAllLayers = () => {
    return {
        type: 'REMOVE_ALL_LAYERS',
    }
}

export const clearLayerFilter = layer => {
    return {
        type: 'CLEAR_LAYER_FILTER',
        layer,
    }
}

export const openLayerFilterModal = layer => {
    return {
        type: 'OPEN_LAYER_FILTER_MODAL',
        layer,
    }
}

export const openModal = layer => {
    return {
        type: 'OPEN_MODAL',
        layer,
    }
}

export const onIconMouseOver = layer => {
    return {
        type: 'ICON_MOUSE_OVER',
        id: layer.id,
    }
}

export const onIconMouseOut = layer => {
    return {
        type: 'ICON_MOUSE_OUT',
        id: layer.id,
    }
}

export const onLoadingParams = layer => {
    return {
        type: 'LOADING_PARAMS',
        id: layer.id,
    }
}

export const onLoadParams = (layer, params) => {
    return {
        type: 'LOAD_PARAMS',
        id: layer.id,
        params,
    }
}

// on ModalContainer.js
export const closeModal = () => {
    return {
        type: 'CLOSE_MODAL',
    }
}

export const getModalData = data => {
    return {
        type: 'GET_MODAL_DATA',
        data,
    }
}

export const changeActiveTab = layer => {
    return {
        type: 'CHANGE_ACTIVE_TAB',
        layer,
    }
}

export const loginUser = data => {
    return {
        type: 'LOGIN_USER',
        data,
    }
}

export const layerFilterLoading = (layer, parameterKey, parameterValue) => {
    return {
        type: 'LAYER_FILTER_LOADING',
        layer,
        parameterKey,
        parameterValue,
    }
}

export const layerFilterLoaded = data => {
    return {
        type: 'LAYER_FILTER_LOADED',
        data,
    }
}

// On Pagination
export const paginate = (layer, page) => {
    return {
        type: 'PAGINATE',
        layer,
        page,
    }
}

// plugins
export const sinalidData = data => {
    return {
        type: 'SINALID_DATA',
        data,
    }
}


<|start_filename|>src/components/SearchLayer/SearchLayer.js<|end_filename|>
import React from 'react'

const SearchLayer = ({onSearchClick, onKeyUpSearch, onBtnCleanSearch, searchString}) => {
    let input
    var searchIconClass = 'search-layer--icon fa fa-'
    var onIconClick = null
    if (searchString !== undefined && searchString.length === 0) {
        searchIconClass += 'search'
    } else {
        searchIconClass += 'close'
        onIconClick = () => {
            input.value = ''
            onBtnCleanSearch()
        }
    }

    const preventSubmit = (e) => {
        e.preventDefault()
    }

    const inputField = node => {
        input = node
        if (node) {
            node.focus()
        }
    }

    const inputOnKeyUp = () => {
        onKeyUpSearch(input.value)
    }

    const inputOnClick = () => {
        onSearchClick()
    }

    return (
        <form action="#" className="search-layer" onSubmit={preventSubmit}>
            <label htmlFor="searchLayer" className="search-layer--title">
                Ou pesquise por aqui:
                <span className={searchIconClass} onClick={onIconClick}></span>
                <input
                    type="text"
                    id="searchLayer"
                    ref={inputField}
                    onClick={inputOnClick}
                    onKeyUp={inputOnKeyUp}
                    className="search-layer--input"
                    placeholder="Ex.: Escolas"/>
            </label>
        </form>
    )
}

export default SearchLayer


<|start_filename|>src/components/MenuHeader/MenuHeader.js<|end_filename|>
import React from 'react'

const MenuHeader = ({onClickMenuHeader}) => {
    return (
        <div className="menu-header">
            <img src={require('../../assets/img/logo.png')} alt="InLoco" className="menu-header--logo"/>
            <a role="button" className="menu-header--button fa fa-times" onClick={onClickMenuHeader}></a>
            <h1 className="menu-header--title">
                Camadas de Exibição
                <small className="menu-header--caption">Escolha uma categoria abaixo</small>
            </h1>
        </div>
    )
}

export default MenuHeader


<|start_filename|>src/components/GooglePlaces/GooglePlacesContainer.js<|end_filename|>
import React from 'react'
import { connect } from 'react-redux'
import GooglePlaces from '../GooglePlaces/GooglePlaces'
import { addGooglePlacesLatLong } from '../../actions/ToolbarActions'

const mapStateToProps = (state, ownProps) => {
    return {
        mapProperties: state.mapProperties,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onAddGooglePlacesLatLong: (latLong) => {
            dispatch(addGooglePlacesLatLong(latLong))
        }
    }
}

const GooglePlacesContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(GooglePlaces)

export default GooglePlacesContainer


<|start_filename|>src/components/Tooltip/Tooltip.js<|end_filename|>
import React from 'react'
import { withContentRect } from 'react-measure'

const calculateDivStyle = (ownHeight, tooltip, scrollTop) => {
    var divStyle = {}
    if (tooltip && tooltip.show) {
        const headerHeight = 131 // TODO dynamic value
        const menuItemHeight = 33 // TODO variable

        let mouseYMinusHeader = tooltip.mouseY - headerHeight
        let index = mouseYMinusHeader / menuItemHeight
        let roundedMouseYMinusHeader = Math.floor(index) * menuItemHeight
        let roundedMouseYTop = roundedMouseYMinusHeader + headerHeight
        let roundedMouseY = roundedMouseYTop + (menuItemHeight / 2)
        let correctTooltipPosition = roundedMouseY - (ownHeight/2)

        divStyle = {
            left: tooltip.sidebarLeftWidth,
            top: correctTooltipPosition,
        }
    }
    return divStyle
}

const Tooltip = withContentRect('bounds')(({measureRef, measure, contentRect, tooltip, scrollTop}) => {
    var className = ''
    var text = ''
    var title = ''
    if (tooltip && tooltip.show) {
        className = 'tooltip left'
        text = tooltip.text === '' ? 'Não tem descrição' : tooltip.text
    }
    return (
        <div  ref={measureRef} className={className} style={calculateDivStyle(contentRect.bounds.height, tooltip, scrollTop)}>
            {text}
        </div>
    )
})

export default Tooltip


<|start_filename|>src/components/SidebarRight/SidebarRightContainer.js<|end_filename|>
import React from 'react'
import { connect } from 'react-redux'
import SidebarRight from './SidebarRight'
import GeoAPI from '../Api/GeoAPI.js'
import {
    clearLayerFilter,
    dropLayer,
    getModalData,
    hideSidebarRight,
    layerFilterLoading,
    layerFilterLoaded,
    onIconMouseOut,
    onIconMouseOver,
    onLoadingParams,
    onLoadParams,
    openLayerFilterModal,
    openModal,
    removeAllLayers,
    slideLayerDown,
    slideLayerUp,
    toggleLayer,
    toggleLayerInformation
} from '../../actions/actions.js'

/**
 * This function filters layers using the selected property
 * @param {Object[]} layers - this is array of layers.
 * @returns {Object[]} - returns all layers that are selected
 */
const selectedLayers = (layers) => {
    if (!Array.isArray(layers)) {
        return []
    }
    return layers.filter(layer => layer.selected)
}

const mapStateToProps = (state, ownProps) => {
    return {
        layers: selectedLayers(state.layers),
        showSidebarRight: state.showSidebarRight,
        orderByLayerOrder: ownProps.orderByLayerOrder,
        lastClickData: state.lastClickData,
        polygonData: state.polygonData,
    }
}

const mapDispatchToProps = (dispatch) => {
    const onAjaxDataFetched = (layerData) => {
        dispatch(getModalData(layerData))
    }
    /**
     * Fetch data from server to get content
     * of the clicked layer
     */
    const onGetModalData = (layer, lastClickData) => {
        const MAX_ITEMS_TO_LOAD = 9999

        let url = GeoAPI.createUrl({
            layer: layer,
            clickData: lastClickData,
            featureCount: MAX_ITEMS_TO_LOAD
        })
        GeoAPI.getLayerData(onAjaxDataFetched, url)
    }

    const onLayerFilterSearchLoaded = data => {
        dispatch(layerFilterLoaded(data))
    }

    return {
        onClearLayerFilter: item => {
            dispatch(clearLayerFilter(item))
        },
        onIconMouseOut: layer => {
            dispatch(onIconMouseOut(layer))
        },
        onIconMouseOver: (e, layer) => {
            dispatch(onIconMouseOver(layer))
        },
        onLayerClick: item => {
            dispatch(toggleLayerInformation(item))
        },
        onLayerDown: item => {
            dispatch(slideLayerDown(item))
        },
        onLayerDrop: (dragged, target) => {
            dispatch(dropLayer(dragged, target))
        },
        onLayerFilterSearch: (layerName, parameterKey, parameterValue) => {
            dispatch(layerFilterLoading(layerName, parameterKey, parameterValue))
            GeoAPI.getLayerFilteredData(layerName, parameterKey, parameterValue, onLayerFilterSearchLoaded)
        },
        onLayerRemove: item => {
            dispatch(toggleLayer(item))
        },
        onLayerUp: item => {
            dispatch(slideLayerUp(item))
        },
        onLoadParams: layer => {
            dispatch(onLoadingParams(layer))
            GeoAPI.getLayerParams(layer, data => {
                let xmlDoc
                if (window.DOMParser) {
                    let parser = new DOMParser()
                    xmlDoc = parser.parseFromString(data, 'text/xml')
                } else {
                    // Internet Explorer
                    xmlDoc = new ActiveXObject('Microsoft.XMLDOM')
                    xmlDoc.async = 'false'
                    xmlDoc.loadXML(data)
                }
                let params = Array.from(
                    xmlDoc.getElementsByTagName('xsd:element')
                ).map(
                    el => el.getAttribute('name')
                )
                params.pop() // remove last one (xsd element of the layer itself)
                dispatch(onLoadParams(layer, params))
            })
        },
        onOpenLayerFilterModal: item => {
            dispatch(openLayerFilterModal(item))
        },
        onOpenModal: (item, lastClickData) => {
            dispatch(openModal(item))
            var selectedLayer = item
            onGetModalData(selectedLayer, lastClickData)
        },
        onRemoveAllLayers: item => {
            dispatch(removeAllLayers())
        },
        onSidebarRightHideClick: () => {
            dispatch(hideSidebarRight())
        },
    }
}

const SidebarRightContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(SidebarRight)

export default SidebarRightContainer


<|start_filename|>src/components/Modal/Contents/News.js<|end_filename|>
import React from 'react'

const News = ({onCloseModal}) => {

    return (
        <div className="news-container">
            <span className="intro"> Bem-vindo a plataforma de mapas interativos do  inLoco 2.0! A nova plataforma é moderna e intuitiva e permite ao usuário visualizar e sobrepor dados geográficos de diversos assuntos, realizar buscas e dispor de diversas informações. Além das consultas de sempre, essa ferramenta traz de forma acessível novas funcionalidades e está disponível para uso no seu dia a dia: </span>
            <ol className="news-modal-list">
                <li>
                    <strong> Desenho livre </strong> – É uma ferramenta rápida e fácil que permite que você desenhe ou demarque um ponto de interesse ou áreas relevantes no mapa.
                </li>
                <li>
                    <strong> Busca por polígono ou área </strong> – Facilita a busca de uma área fechada em determinada região e a descreve com varias informações num raio de distância definido no mapa.
                </li>

                <li>
                    <strong> Busca de endereço  </strong> – É um serviço que permite buscar um endereço ou um ponto de interesse, através de uma busca textual.
                </li>

                <li>
                    <strong> Mudar a camada de fundo </strong> – É possível alterar o estilo de plano de fundo do mapa oferecendo imagens por satélite, terrenos ou logradouros do Estado do Rio de Janeiro.
                </li>

                <li>
                    <strong> Google Street View </strong> – A nova ferramenta agora está integrada ao Street View, que é um recurso do Google. Ela disponibiliza vistas panorâmicas de 360° e permite que os usuários vejam partes de algumas regiões ao nível do chão/solo.
                </li>
            </ol>
            {/*
            * Date.now of last update
            */}
            <div className="modal-options--back">
                <button id="newsTimestamp" className="modal-options--link" data-value="1505847454072" onClick={onCloseModal}>
                    Fechar e não exibir novamente
                </button>
            </div>
        </div>
    )
}

export default News


<|start_filename|>src/components/ShareUrl/ShareUrl.js<|end_filename|>
import React from 'react'
import ClipboardButton from 'react-clipboard.js'

/**
 * Filters layers using the `selected` property
 * @param {Object[]} layers - layers array
 * @returns {Object[]} - returns all layers that are selected
 */
const selectedLayers = (layers) => {
    if (!Array.isArray(layers)) {
        return []
    }
    return layers.filter(layer => layer.selected)
}

/**
 * Truncate coordinate values to given precision, without rounding.
 * For instance, -23.1234567890 => -23.123456 when decimals = 6.
 * Note that it wont create zeroes on the end, so -23.45 => -23.45 when decimals = 6.
 * @param {number} number a number to be truncated
 * @param {number} decimals desired decimals on truncated number
 * @return {string} the truncated number as string.
 */
const truncateValue = (number, decimals) => {
    let re = new RegExp('^-?\\d+(?:\.\\d{0,' + (decimals || -1) + '})?')
    return number.toString().match(re)[0]
}

/**
 * Creates a share URL with current map view and active layers
 * @param {Object} mapProperties object with map properties
 * @param {Object} mapProperties.currentCoordinates object with the current view lat/lon and zoom level
 * @param {number} mapProperties.currentCoordinates.lat current view latitude
 * @param {number} mapProperties.currentCoordinates.lng current view longitude
 * @param {number} mapProperties.currentCoordinates.zoom current view zoom
 * @param {Object[]} activeLayers array with active layers
 * @return {string} JSX string
 */
const ShareUrl = ({mapProperties, layers, orderByLayerOrder, onToolbarItemClick}) => {
    if (!mapProperties || !mapProperties.currentCoordinates) {
        return null
    }
    let lat = truncateValue(mapProperties.currentCoordinates.lat, 6)
    let lng = truncateValue(mapProperties.currentCoordinates.lng, 6)
    let zoom = mapProperties.currentCoordinates.zoom
    let basemap = mapProperties.currentMap.name

    // drop current value if needed
    let baseUrl = location.href.split('#')[0]

    let url = `${baseUrl}#lat=${lat}&lng=${lng}&zoom=${zoom}&basemap=${basemap}`

    let activeLayers = orderByLayerOrder(selectedLayers(layers)).map(l => {
        // ex: plataforma_educ_escolas:educ_escolas_publicas
        let layerString = `${l.id}:${l.styles[l.selectedLayerStyleId].name.replace('plataforma:', '')}`
        if (l.filterKey && l.filterValue) {
            // ex: plataforma_educ_escolas:educ_escolas_publicas(Escola|maria)
            layerString += `(${l.filterKey}|${l.filterValue})`
        }
        return layerString
    }).join(',')

    if (activeLayers) {
        url += `&layers=${activeLayers}`
    }

    if (mapProperties.placeToCenter) {
        if (mapProperties.placeToCenter.cd_craai) {
            url += `&craai=${mapProperties.placeToCenter.cd_craai}`
        }
        if (mapProperties.placeToCenter.cd_municipio) {
            url += `&municipio=${mapProperties.placeToCenter.cd_municipio}`
        }
        if (mapProperties.placeToCenter.cd_bairro) {
            url += `&bairro=${mapProperties.placeToCenter.cd_bairro}`
        }
        if (mapProperties.placeToCenter.tipo === 'ORGAO') {
            url += `&orgao=${mapProperties.placeToCenter.id}`
        }
    }

    return (
        <fieldset className="toolbar-inputshare">
            <ClipboardButton data-clipboard-text={url} onClick={()=> onToolbarItemClick('share')}>COPIAR</ClipboardButton>
        </fieldset>
    )
}

export default ShareUrl


<|start_filename|>src/components/BaseMapList/BaseMapList.js<|end_filename|>
import React from 'react'

const BaseMapList = ({baseMaps, onChangeActiveBaseMap}) => {

    function handleChangeActiveBaseMap(baseMap) {
        return onChangeActiveBaseMap(baseMap)
    }

    return (
        <ul className="basemap-list">
            {
                // Checks if baseMaps already exists in state
                baseMaps
                ? baseMaps.map((baseMap, index) => {
                    return (
                        <li className="basemap-list--item" key={index}>
                            <a role="button" onClick={()=>handleChangeActiveBaseMap(baseMap)}>
                                <img src={baseMap.image} className="basemap-list--image" alt={`${baseMap.name} base map`}/>
                            </a>
                            <span className="tooltip top">{baseMap.subtitle}</span>
                        </li>
                    )
                })
                : ''
            }
        </ul>
    )
}

export default BaseMapList


<|start_filename|>src/components/Api/SinalidAPI.js<|end_filename|>
import axios from 'axios'

const SinalidAPI = {
    /**
     * Gets a list of Missing People from Sinalid API
     * @param {function} callback Callback function to execute
     * @param {number} dpId Delegacia de Polícia ID
     */
    listMissingPeople(callback, dpId) {
        axios
            .get(`/sinalid/api/listadesaparecidos/${dpId}`)
            .then(response => callback(response))
            .catch(error => callback(error.response))
    }
}

export default SinalidAPI


<|start_filename|>src/components/GooglePlaces/GooglePlaces.js<|end_filename|>
import React, {Component} from 'react'

export default class GooglePlaces extends Component {
    constructor(props) {
        super(props)
    }

    preventSubmit(e) {
        e.preventDefault()
    }

    onPlacesChanged(searchBox) {
        var places = searchBox.getPlaces()
        var viewport = places[0].geometry.viewport

        var nelat = viewport.getNorthEast().lat()
        var nelng = viewport.getNorthEast().lng()
        var swlat = viewport.getSouthWest().lat()
        var swlng = viewport.getSouthWest().lng()

        return [
            [
                nelat, nelng
            ],
            [
                swlat, swlng
            ]
        ]
    }

    componentDidMount() {
        const { onAddGooglePlacesLatLong } = this.props
        const onPlacesChanged = this.onPlacesChanged

        const defaultBounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(-23.3926, -44.8935),
            new google.maps.LatLng(-20.7916, -40.7384)
        )

        let input = document.getElementById("GooglePlacesSearch")
        let searchBox = new google.maps.places.SearchBox(input, {
            bounds: defaultBounds
        })

        // Listen for the event fired when the user selects a prediction and retrieve
        // more details for that place.
        searchBox.addListener('places_changed', function() {
            onAddGooglePlacesLatLong(onPlacesChanged(this))
        })
    }

    render() {
        return (
            <form
                action=""
                onSubmit={this.preventSubmit}
                className="google-places"
            >
                <label
                    htmlFor="GooglePlacesSearch"
                    className="google-places--label"
                >
                    Busca por ponto de interesse ou logradouro:
                </label>
                <input
                    type="text"
                    id="GooglePlacesSearch"
                    placeholder="Ex.: Estações de metrô, edifícios públicos...."
                    className="google-places--input"
                />
            </form>
        )
    }
}


<|start_filename|>src/components/DataTable/DataTable.js<|end_filename|>
import React from 'react'

// how many columns are visible when used within right sidebar
const COLLAPSED_COLUMNS_COUNT = 3

/**
 * Returns the correct property of layer to be shown on table.
 * @param {boolean} isCollapsed If DataTable is collapsed or not
 * @return {string}
 */
const featureType = (isCollapsed) => isCollapsed ? 'features' : 'modal'

/**
 * Returns an array of properties' keys to be used as table header
 * @param {Object} layer - Layer data.
 * @param {Array} [layer.table] - An array of strings specifying columns to be shown on the table. It can be undefined or not set.
 * @param {string} [layer.table[]] - A column name.
 * @param {Array} layer.features - Array of returned objects. By default only three objects are loaded when DataTable is used within SidebarRight.
 * @param {Object} layer.features[] - A returned object from GeoServer.
 * @param {Object} layer.features[].properties - An object with keys/values of this feature on GeoServer.
 * @param {boolean} isCollapsed If the table is collapsed (within right sidebar) or expanded (within modal).
 * @return {Array<String>} An array with the headers strings.
 */
const layerHeaders = (layer, isCollapsed, isLayerFilter) => {
    let headers = []

    // if layer has table columns keyword, indicating which columns need to be shown (only if layer is collapsed)
    if (layer.table && isCollapsed) {
        // just use it
        headers.push(...layer.table)
    } else {
        if (isLayerFilter) {
            headers.push(...Object.keys(layer.filteredData[0].properties))
        } else {
            // Use first object to get properties keys
            if (isCollapsed) {
                headers.push(...Object.keys(layer[featureType(isCollapsed)][0].properties))
            } else {
                headers.push(...Object.keys(layer[featureType(isCollapsed)].pages[0][0].properties))
            }
        }
    }

    // replace _ for spaces
    headers = headers.map(header => header.replace(/_/g, ' '))

    // slice array if needed
    if (isCollapsed) {
        headers = headers.slice(0, COLLAPSED_COLUMNS_COUNT)
    }

    return headers
}

/**
 * Returns an array of features values to be used as table row
 * @param {Object} feature A returned object from GeoServer.
 * @param {Object} feature.properties - An object with keys/values of this feature on GeoServer.
 * @param {Array} headers The headers array to match with feature values
 * @param {string} headers[] A column header, either from layer.table or from layer.features[0].properties
 * @return {Array<String>} An array with feature data strings
 */
const featureData = ((feature, headers) => {

    // keys_are_written_with_underscores
    let keys = headers.map(header => header.replace(/ /g, '_'))
    let values = []

    // for each key, find it's respective value
    keys.map(key => {
        values.push(feature.properties[key])
    })

    return values
})

const round = (number, precision) => {
    let factor = Math.pow(10, precision)
    let tempNumber = number * factor
    let roundedTempNumber = Math.round(tempNumber)
    return roundedTempNumber / factor
}

const isFloat = text => {
    if (isNaN(parseInt(text, 10))) {
        return false
    }
    if (parseInt(text, 10) === parseFloat(text, 10)) {
        return false
    }
    return true
}

/**
 * Test if string is an URL
 * @param {Object} text The text to be tested
 * @return {boolean} - if the string is an URL or not
 */
const isURL = text => {
    if (!text) {
        return false
    }
    return text.toString().substr(0,7) === 'http://' || text.toString().substr(0,8) === 'https://'
}

/**
 * Test if string is an URL that doesn't start with 'http://'
 * @param {Object} text The text to be tested
 * @return {boolean} - if the string is a broken URL or not
*/
const isBrokenURL = text => {
    if (!text) {
        return false
    }
    return text.toString().substr(0,4) === 'www.'
}

/**
 * Parse text to be shown on cell
 * @param {Object} text The text to be shown on cell
 * @return {string} - JSX string with the cell code
 */
const parseContent = text => {
    if (isURL(text)) {
        return (<a href={text} target="_blank">Link</a>)
    } else if (isBrokenURL(text)) {
        return (<a href={'http://' + text} target="_blank">Link</a>)
    } else if (isFloat(text)) {
        return (round(parseFloat(text, 10), 1)).toString().replace('.', ',')
    }
    return text
}

const renderHeader = ({headers}) => {
    return <thead>
        <tr>
            {
                headers.map((property, index) => {
                    return <th className="data-table--header" key={index}>{property}</th>
                })
            }
        </tr>
    </thead>
}

const renderBody = ({layer, isCollapsed, headers, isLayerFilter}) => {
    let pageToRender

    if (isLayerFilter) {
        pageToRender = layer.filteredData
    } else {
        if (isCollapsed) {
            pageToRender = layer[featureType(isCollapsed)]
        } else {
            pageToRender = layer[featureType(isCollapsed)].pages[layer.modal.currentPage]
        }
    }

    return (
        <tbody>
        {
            pageToRender.map((feature, indexFeature) => {
                return <tr className="data-table--row" key={indexFeature}>
                    {
                        featureData(feature, headers).map((property, indexProperty) => {
                            return <td className="data-table--body" key={indexProperty}>{parseContent(property)}</td>
                        })
                    }
                </tr>
            })
        }
        </tbody>
    )
}

const validPages = (currentPage, totalPages) => {
    let arr = []

    if (currentPage - 2 >= 0) {
        arr.push(currentPage - 2)
    }

    if (currentPage - 1 >= 0) {
        arr.push(currentPage - 1)
    }

    arr.push(currentPage)

    if (currentPage +1 < totalPages) {
        arr.push(currentPage + 1)
    }

    if (currentPage + 2 < totalPages) {
        arr.push(currentPage + 2)
    }

    return arr
}

const renderPagination = ({layer, isCollapsed, handlePaginate, isLayerFilter}) => {
    if (isCollapsed || isLayerFilter) {
        return null
    }

    let page = layer.modal.currentPage
    let totalPages = layer.modal.pages.length
    let totalItems = layer.modal.totalItemsCount
    let pageFirstItemNumber = page * 5 + 1
    let pageLastItemNumber = (page + 1) * 5

    if (pageLastItemNumber > totalItems) {
        pageLastItemNumber = totalItems
    }

    return (
        <div className="modal-pagination-container">
            <ul className="modal-pagination">
                <li className="modal-pagination--item">
                    <button className="modal-pagination--link" onClick={() => handlePaginate(layer,page-1)} disabled={page === 0}>
                        <span className="fa fa-chevron-left"></span>
                    </button>
                </li>
                {
                    validPages(page, totalPages).map((n, index) => {
                        let className = "modal-pagination--link"
                        if (page === n) {
                            className += ' active'
                        }
                        return (
                            <li key={index} className="modal-pagination--item">
                                <button className={className} onClick={() => handlePaginate(layer,n)} disabled={n === page}>{n+1}</button>
                            </li>
                        )
                    })
                }
                <li className="modal-pagination--item">
                    <button className="modal-pagination--link" onClick={() => handlePaginate(layer,page+1)} disabled={page === totalPages-1}>
                        <span className="fa fa-chevron-right"></span>
                    </button>
                </li>
            </ul>
            <span className="modal-pagination--counter">
                Exibindo itens {pageFirstItemNumber} a {pageLastItemNumber} de {totalItems}
            </span>
        </div>
    )
}

/**
 * Renders element
 * @param {Object} param Parameter object being destructured
 * @param {Object} param.layer - Layer data.
 * @param {Array} [param.layer.table] - An array of strings specifying columns to be shown on the table. It can be undefined or not set.
 * @param {string} [param.layer.table[]] - A column name.
 * @param {Array} param.layer.features - Array of returned objects. By default only three objects are loaded when DataTable is used within SidebarRight.
 * @param {Object} param.layer.features[] - A returned object from GeoServer.
 * @param {Object} param.layer.features[].properties - An object with keys/values of this feature on GeoServer.
 * @param {boolean} param.isCollapsed - If the table is collapsed (within right sidebar) or expanded (within modal)
 * @return {string} - JSX string with the component code
 */
const DataTable = ({layer, isCollapsed, handlePaginate, isLayerFilter}) => {

    let headers = layerHeaders(layer, isCollapsed, isLayerFilter)

    if (!layer[featureType(isCollapsed)] && !layer.filteredData) {
        return null
    }
    return (
        <div>
            <div className="data-table-container">
                <table className="data-table">
                    {
                        renderHeader({headers})
                    }
                    {
                        renderBody({layer, isCollapsed, headers, isLayerFilter})
                    }
                </table>
            </div>
            { renderPagination({layer, isCollapsed, handlePaginate, isLayerFilter}) }
        </div>
    )
}

export default DataTable


<|start_filename|>src/components/LayerSubtitle/LayerSubtitle.js<|end_filename|>
import React from 'react'
import { DragSource } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import Charts from '../Charts/Charts.js'
import DataTable from '../DataTable/DataTable.js'
import Sinalid from '../Sinalid/Sinalid.js'
import LayerStylesCarouselContainer from '../LayerStylesCarousel/LayerStylesCarouselContainer.js'

const layerSubtitleSource = {
    beginDrag(props) {
        return {id: props.layer.key}
    },
    endDrag(props, monitor) {
        let dragged = props.layer
        let dropResult = monitor.getDropResult()
        if(dropResult){
            let target = dropResult.target
            props.onLayerDrop(dragged, target)
        }
    },
}

function collect(connect, monitor) {
    return {
        connectDragSource: connect.dragSource(),
        isDragging: monitor.isDragging()
    }
}

const LayerSubtitle = ({
    layer,
    connectDragSource,
    lastClickData,
    isDragging,
    onClearLayerFilter,
    onIconMouseOut,
    onIconMouseOver,
    onLayerClick,
    onLayerDown,
    onLayerDrop,
    onLayerFilterSearch,
    onLayerRemove,
    onLayerUp,
    onLoadParams,
    onOpenLayerFilterModal,
    onOpenModal,
}) => {
    let selectedStyle = layer ? layer.styles[layer.selectedLayerStyleId] : {}
    let layerSubtitleURL = layer ? `/geoserver/plataforma/wms?tiled=true&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&EXCEPTIONS=application%2Fvnd.ogc.se_xml&FORMAT=image%2Fpng&LAYER=${layer.layerName}&STYLE=${selectedStyle.name}` : ''
    let description = {
        // replace \n for <br>
        __html: layer ? layer.description.replace(/(?:\r\n|\r|\n)/g, '<br />') : ''
    }

    function handleClearLayerFilter() {
        return onClearLayerFilter(layer)
    }

    function handleItemClick() {
        return onLayerClick(layer)
    }

    function handleLayerUp() {
        return onLayerUp(layer)
    }

    function handleLayerDown() {
        return onLayerDown(layer)
    }

    function handleLayerRemove() {
        return onLayerRemove(layer)
    }

    function handleOpenModal() {
        return onOpenModal(layer, lastClickData)
    }

    function handleOpenLayerFilterModal() {
        return onOpenLayerFilterModal(layer)
    }

    let layerItemClassName = 'layer-item'
    if (layer && layer.showInformation) {
        layerItemClassName += ' selected'
    }

    // load filter params from GeoServer
    if (!layer.params && !layer.isLoadingParams) {
        onLoadParams(layer)
    }

    // check if reading filter params from initialization and load them
    if (layer.filterKey && layer.filterValue && !layer.isLoadingFilter && !layer.filteredData) {
        onLayerFilterSearch(layer.name, layer.filterKey, layer.filterValue)
    }

    return connectDragSource(
        <div className={layerItemClassName}>
            <div
                className="layer-item-header"
                onClick={layer => handleItemClick()}
            >
                <h2 className="layer-item-header--title">
                    Grupo: {layer ? layer.menu2.join(' - ') : ''}
                    <small className="layer-item-header--caption">{layer ? layer.title : ''}</small>
                </h2>
                <span className="layer-item-header--icon"></span>
            </div>
            <div className="layer-item-body">
                <div className="layer-item-controls">
                    <button
                        aria-label="Subir camada"
                        className="layer-item-controls-button up"
                        onClick={layer => handleLayerUp()}
                    >
                        <i className="fa fa-chevron-up" aria-hidden="true"></i>
                    </button>
                    <button
                        aria-label="Descer camada"
                        className="layer-item-controls-button down"
                        onClick={layer => handleLayerDown()}
                    >
                        <i className="fa fa-chevron-down" aria-hidden="true"></i>
                    </button>
                    <button
                        aria-label="Remover camada"
                        className="layer-item-controls-button remove"
                        onClick={layer => handleLayerRemove()}
                    >
                        <i className="fa fa-trash-o" aria-hidden="true"></i>
                    </button>
                </div>
                <div className="layer-item--legend">
                    <img className="layer-item--legend-img" src={layerSubtitleURL} alt="" onMouseOver={e => onIconMouseOver(e, layer)} onMouseOut={() => onIconMouseOut(layer)}/>
                </div>
                <div className="layer-item-more-info">
                    <h3 className="layer-item-more-info--title">Exibições da camada</h3>
                    <LayerStylesCarouselContainer layer={layer}/>
                    <div className="layer-item-more-container">
                        <p className="layer-item-more-info--style-title">{selectedStyle.title || ''}</p>
                        <p className="layer-item-more-info--text">{selectedStyle.description || ''}</p>
                    </div>
                    {layer.isLoadingParams ? <p>Aguarde, carregando filtros...</p> : null}
                    {layer.params ?
                        <div className="layer-item-filter">
                            {layer.filterKey && layer.filterValue ?
                                 <div>
                                    <button onClick={handleClearLayerFilter} className="filled">
                                        <span>Remover Filtro</span>
                                        <i className="fa fa-times-circle" aria-hidden="true"></i>
                                    </button>
                                    <h3>Filtros na camada</h3>
                                    <p>Exibindo resultados onde {layer.filterKey} contém "{layer.filterValue}"</p>
                                </div>
                            :
                                <div>
                                    <button onClick={handleOpenLayerFilterModal} className="empty">
                                        <span>Selecionar Filtro</span>
                                        <i className="fa fa-filter" aria-hidden="true"></i>
                                    </button>
                                    <h3>Sem filtro na camada</h3>
                                </div>
                            }
                        </div>
                    : null }
                    {
                        layer.features
                        ? <div className="layer-item-data">
                            <h3 className="layer-item-data--title">Dados do registro</h3>
                            <DataTable layer={layer} isCollapsed={true}/>
                            <a role="button"
                                className="layer-item-data--more-info"
                                onClick={handleOpenModal}>ver mais</a>
                            <Charts layer={layer}/>
                            {layer.id === 'plataforma_inst_sinalid' ?
                                <Sinalid layer={layer}/>
                            : ''}
                        </div>
                        : ''
                    }
                </div>
            </div>
        </div>
    )
}

// Set component as a drag source. It says that it is draggable.
// We also need to pass three parameters: "layerSubtitle" is a string
// that works as an id to match source with destiny.
export default DragSource("layerSubtitle", layerSubtitleSource, collect)(LayerSubtitle)


<|start_filename|>src/components/LayerStylesCarousel/LayerStylesCarouselContainer.js<|end_filename|>
import React from 'react'
import LayerStylesCarousel from './LayerStylesCarousel'
import { connect } from 'react-redux'
import { slideLeftStyles, slideRightStyles, selectLayerStyle } from '../../actions/actions.js'

const mapStateToProps = (state, ownProps) => {
    return {
        layer: ownProps.layer,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onArrowLeftClick: (item) => {
            dispatch(slideLeftStyles(item))
        },
        onArrowRightClick: (item) => {
            dispatch(slideRightStyles(item))
        },
        onStyleClick: (item, styleId) => {
            dispatch(selectLayerStyle(item, styleId))
        },
    }
}

const LayerStylesCarouselContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(LayerStylesCarousel)

export default LayerStylesCarouselContainer
