| "react-dom": "^17.0.2", | "react-dom": "^17.0.2", | ||||
| "react-helmet-async": "^1.0.9", | "react-helmet-async": "^1.0.9", | ||||
| "react-i18next": "^11.10.0", | "react-i18next": "^11.10.0", | ||||
| "react-modal": "^3.14.3", | |||||
| "react-redux": "^7.2.4", | "react-redux": "^7.2.4", | ||||
| "react-responsive-carousel": "^3.2.19", | "react-responsive-carousel": "^3.2.19", | ||||
| "react-router-dom": "^5.2.0", | "react-router-dom": "^5.2.0", | ||||
| "yup": "^0.32.9" | "yup": "^0.32.9" | ||||
| }, | }, | ||||
| "scripts": { | "scripts": { | ||||
| "start": "set PORT=5500 && react-scripts start ", | |||||
| "start": "set PORT=5500 && react-scripts start ", | |||||
| "build": "react-scripts build", | "build": "react-scripts build", | ||||
| "test": "react-scripts test", | "test": "react-scripts test", | ||||
| "eject": "react-scripts eject" | "eject": "react-scripts eject" |
| import React from 'react'; | |||||
| import PropTypes from 'prop-types'; | |||||
| import Modal from 'react-modal'; | |||||
| const customStyles = { | |||||
| content: { | |||||
| top: '50%', | |||||
| left: '50%', | |||||
| right: 'auto', | |||||
| bottom: 'auto', | |||||
| marginRight: '-50%', | |||||
| transform: 'translate(-50%, -50%)', | |||||
| }, | |||||
| }; | |||||
| const CustomModal = ({ showModal, handleClose, handleConfirm }) => { | |||||
| console.log('show in modal', showModal) | |||||
| return ( | |||||
| <Modal | |||||
| isOpen={showModal} | |||||
| onRequestClose={handleClose} | |||||
| style={customStyles} | |||||
| > | |||||
| <h3>There is more than 100 results!</h3> | |||||
| {/* <h5>There is more than 100 results</h5> */} | |||||
| <div className='row' style={{ justifyContent: 'space-between', marginTop: '30%' }}> | |||||
| <button type='button' className="btn btn-sm btn-primary" onClick={handleConfirm}><span className='text-lg'>Confirm and request</span></button> | |||||
| <button type='button' className="btn btn-sm btn-primary" onClick={handleClose}><span className='text-lg'>Cancel</span></button> | |||||
| </div> | |||||
| </Modal> | |||||
| ) | |||||
| } | |||||
| CustomModal.propTypes = { | |||||
| showModal: PropTypes.bool, | |||||
| handleClose: PropTypes.func, | |||||
| handleConfirm: PropTypes.func | |||||
| }; | |||||
| export default CustomModal; |
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| return ( | return ( | ||||
| <tr> | |||||
| <td> | |||||
| <p> | |||||
| {console.log(scrape)} | |||||
| </p><h3><Link to={{ | |||||
| pathname: SCRAPE_RESULTS_PAGE.replace(':id', scrape._id), | |||||
| id: scrape._id | |||||
| }} | |||||
| target="_blank" rel="noopener noreferrer">{scrape.description || 'No name given'}</Link></h3> | |||||
| <p> | |||||
| {scrape.location && <span className="mr-2">Location: <span className='text-info'>{scrape.location}</span></span>} | |||||
| <a className='mr-1' href={scrape.sourceUrl} target="_blank" rel="noopener noreferrer"><i className="fas fa-external-link-square-alt"></i> https://www.apartments.com</a> | |||||
| {/* <span className="text-lg">Id: {scrape._id}</span> */} | |||||
| </p> | |||||
| <p> | |||||
| <span className="mr-1">Count: <span className='text-info'>{scrape.count}</span></span> | |||||
| {scrape.createDate && <span className="mr-1">Creation time: <span className='text-info'>{(new Date(scrape.createDate)).toLocaleString()}</span></span>} | |||||
| {(scrape.startDate !== undefined && scrape.startDate !== null) ? | |||||
| <span className="mr-1">Time: <span className="text-info">{(new Date(scrape.startDate)).toLocaleString()} - {(scrape.endDate != undefined && scrape.endDate !== null) ? (new Date(scrape.endDate)).toLocaleString() : ''}</span> </span> | |||||
| : <span className="mr-1">{t('scrapeRequest.EstimatedTime')} <span className='text-info'>{(new Date(scrape.estimate)).toLocaleString()}</span></span> | |||||
| } | |||||
| </p> | |||||
| </td> | |||||
| <td> | |||||
| {scrape.filters.map(element => ( | |||||
| element.value && <span key={element.value} className="badge bg-primary m-1"><span>{element.name}:</span> <br></br> <span>{element.name === 'baths' ? element.value + '+' : element.value}</span></span> | |||||
| ))} | |||||
| </td> | |||||
| <td> | |||||
| <ScrappeStatus status={scrape.status} id={scrape._id} handleExecute={handleExecute} /> | |||||
| </td> | |||||
| </tr> | |||||
| <> | |||||
| <tr> | |||||
| <td> | |||||
| <p> | |||||
| </p><h3><Link to={{ | |||||
| pathname: SCRAPE_RESULTS_PAGE.replace(':id', scrape._id), | |||||
| id: scrape._id | |||||
| }} | |||||
| target="_blank" rel="noopener noreferrer">{scrape.description || 'No name given'}</Link></h3> | |||||
| <p> | |||||
| {scrape.location && <span className="mr-2">Location: <span className='text-info'>{scrape.location}</span></span>} | |||||
| <a className='mr-1' href={scrape.sourceUrl} target="_blank" rel="noopener noreferrer"><i className="fas fa-external-link-square-alt"></i> https://www.apartments.com</a> | |||||
| {/* <span className="text-lg">Id: {scrape._id}</span> */} | |||||
| </p> | |||||
| <p> | |||||
| <span className="mr-1">Count: <span className='text-info'>{scrape.count}</span></span> | |||||
| {scrape.createDate && <span className="mr-1">Creation time: <span className='text-info'>{(new Date(scrape.createDate)).toLocaleString()}</span></span>} | |||||
| {(scrape.startDate !== undefined && scrape.startDate !== null) ? | |||||
| <span className="mr-1">Time: <span className="text-info">{(new Date(scrape.startDate)).toLocaleString()} - {(scrape.endDate != undefined && scrape.endDate !== null) ? (new Date(scrape.endDate)).toLocaleString() : ''}</span> </span> | |||||
| : <span className="mr-1">{t('scrapeRequest.EstimatedTime')} <span className='text-info'>{(new Date(scrape.estimate)).toLocaleString()}</span></span> | |||||
| } | |||||
| </p> | |||||
| </td> | |||||
| <td> | |||||
| {scrape.filters.map(element => ( | |||||
| element.value && <span key={element.value} className="badge bg-primary m-1"><span>{element.name}:</span> <br></br> <span>{element.name === 'baths' ? element.value + '+' : element.value}</span></span> | |||||
| ))} | |||||
| </td> | |||||
| <td> | |||||
| <ScrappeStatus status={scrape.status} id={scrape._id} handleExecute={handleExecute} /> | |||||
| </td> | |||||
| </tr> | |||||
| </> | |||||
| ); | ); | ||||
| } | } | ||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import ScrappeStatus from '../../components/ScrapeRequest/ScrappeStatus' | import ScrappeStatus from '../../components/ScrapeRequest/ScrappeStatus' | ||||
| const ScrappeDetails = ({ details }) => { | |||||
| const ScrappeDetails = ({ details, handleExecuteScrappes }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| console.log('details', details) | |||||
| return ( | return ( | ||||
| (details) ? | (details) ? | ||||
| <div className='container'> | <div className='container'> | ||||
| <div className="row mb-2"> | <div className="row mb-2"> | ||||
| <div className="col-sm-10"> | <div className="col-sm-10"> | ||||
| <h2 className="m-0"> Scrappe <a href={details.sourceUrl} className='text-lg'> {details.sourceUrl} </a></h2> | |||||
| <h2 className="m-0">{details.description}</h2> | |||||
| <a href={details.sourceUrl} className='text-lg'> {details.sourceUrl} </a> | |||||
| <br /> | <br /> | ||||
| <div className="row m-0"> | <div className="row m-0"> | ||||
| <div className="com-md-4"> | <div className="com-md-4"> | ||||
| (details.location) ? | (details.location) ? | ||||
| <h3>{details.location}</h3> | <h3>{details.location}</h3> | ||||
| : ''} | : ''} | ||||
| {details.createDate && <div><span className="mr-2">Creation date: {(new Date(details.createDate)).toLocaleString()}</span></div>} | |||||
| { | { | ||||
| (details.startDate !== undefined && details.startDate !== null) ? | (details.startDate !== undefined && details.startDate !== null) ? | ||||
| <span className="mr-2">Time: <span className="">{(new Date(details.startDate)).toLocaleString() } - {(details.endDate != undefined && details.endDate!==null) ? (new Date(details.endDate)).toLocaleString():''}</span> </span> | |||||
| :(details.estimate) ? | |||||
| <span className="text-muted">{t('scrapeRequest.EstimatedTime')} {(new Date(details.estimate)).toLocaleString()}</span> | |||||
| : '' | |||||
| <span className="mr-2">Time: <span className="">{(new Date(details.startDate)).toLocaleString()} - {(details.endDate != undefined && details.endDate !== null) ? (new Date(details.endDate)).toLocaleString() : ''}</span> </span> | |||||
| : (details.estimate) ? | |||||
| <span className="text-muted">{t('scrapeRequest.EstimatedTime')} {(new Date(details.estimate)).toLocaleString()}</span> | |||||
| : '' | |||||
| } | } | ||||
| </div> | </div> | ||||
| <div className="col-md-4"> | <div className="col-md-4"> | ||||
| : ''} | : ''} | ||||
| </div> | </div> | ||||
| <div className="col-md-1"> | <div className="col-md-1"> | ||||
| {details.status ? <ScrappeStatus status={details.status} /> : ''} | |||||
| {details.status ? <ScrappeStatus handleExecute={handleExecuteScrappes} id={details._id} status={details.status} /> : ''} | |||||
| <div className="col-md-16 mt-2"> | |||||
| {details.status === 'done' ? <a href={`${process.env.REACT_APP_BASE_API_URL}scrapes/${details._id}/files/xml`} download><span className='text-md'>XML result</span></a> : ''} | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| } | } | ||||
| ScrappeDetails.propTypes = { | ScrappeDetails.propTypes = { | ||||
| details: PropTypes.object | |||||
| details: PropTypes.object, | |||||
| handleExecuteScrappes: PropTypes.func | |||||
| }; | }; | ||||
| export default ScrappeDetails; | export default ScrappeDetails; |
| import React, { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||
| import CreateScrapeRequest from '../../components/CreateScrapeRequest/CreateScrapeRequest'; | import CreateScrapeRequest from '../../components/CreateScrapeRequest/CreateScrapeRequest'; | ||||
| import ScrapeRequests from '../../components/ScrapeRequests/ScrapeRequests'; | import ScrapeRequests from '../../components/ScrapeRequests/ScrapeRequests'; | ||||
| import { createScrappes, executeScrappes, getAllScrappes } from '../../request/scrappe'; | |||||
| import { createScrappes, estimateScrappes, executeScrappes, getAllScrappes } from '../../request/scrappe'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import Select from 'react-select' | import Select from 'react-select' | ||||
| import { scrapeFrom } from '../../constants/filters'; | import { scrapeFrom } from '../../constants/filters'; | ||||
| import CustomModal from '../../components/Modals/Modal'; | |||||
| const HomePage = () => { | const HomePage = () => { | ||||
| const [scrappes, setScrappes] = useState([]) | const [scrappes, setScrappes] = useState([]) | ||||
| const [showModal, setShowModal] = useState(false) | |||||
| const [request, setRequest] = useState() | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| getAllScrappes().then(res => setScrappes(res.data)) | getAllScrappes().then(res => setScrappes(res.data)) | ||||
| const interval = setInterval(() => { | const interval = setInterval(() => { | ||||
| async function handleRequest(reqObj) { | async function handleRequest(reqObj) { | ||||
| console.log(reqObj) | |||||
| const res = await createScrappes(reqObj) | |||||
| // console.log(reqObj) | |||||
| const res = await estimateScrappes(reqObj) | |||||
| if (res.status === 200) { | if (res.status === 200) { | ||||
| if (res.data.count > 100) { | |||||
| setShowModal(true) | |||||
| setRequest(reqObj) | |||||
| } | |||||
| else { | |||||
| const res1 = await createScrappes(reqObj) | |||||
| if (res1.status === 200) { | |||||
| getAllScrappes().then(res => setScrappes(res.data)) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| async function handleConfirm() { | |||||
| const res1 = await createScrappes(request) | |||||
| // console.log("handleConfirm", res1) | |||||
| if (res1.status === 200) { | |||||
| getAllScrappes().then(res => setScrappes(res.data)) | getAllScrappes().then(res => setScrappes(res.data)) | ||||
| } | } | ||||
| setShowModal(false) | |||||
| } | } | ||||
| async function handleExecute(id) { | async function handleExecute(id) { | ||||
| return ( | return ( | ||||
| <> | <> | ||||
| <CustomModal showModal={showModal} handleClose={() => setShowModal(false)} handleConfirm={handleConfirm} /> | |||||
| <div className='content' > | <div className='content' > | ||||
| <div className="container"> | <div className="container"> | ||||
| <h1 className="lead text-center" style={{ fontSize: '80px' }}>{t('scrapeRequests.Columns.Scrape')} </h1> | <h1 className="lead text-center" style={{ fontSize: '80px' }}>{t('scrapeRequests.Columns.Scrape')} </h1> |
| import React, { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||
| import { getByIdScrappe } from '../../request/scrappe'; | |||||
| import { Link , useParams } from "react-router-dom"; | |||||
| import { executeScrappes, getByIdScrappe } from '../../request/scrappe'; | |||||
| import { Link, useParams } from "react-router-dom"; | |||||
| import ScrappeDetails from '../../components/ScrappeDetails/ScrappeDetails' | import ScrappeDetails from '../../components/ScrappeDetails/ScrappeDetails' | ||||
| import ScrappeResult from '../../components/ScrappeResult/ScrappeResult' | import ScrappeResult from '../../components/ScrappeResult/ScrappeResult' | ||||
| const [scrappeDetails, setScrappeDetails] = useState() | const [scrappeDetails, setScrappeDetails] = useState() | ||||
| useEffect(() => { | useEffect(() => { | ||||
| let interval; | |||||
| getByIdScrappe(id) | getByIdScrappe(id) | ||||
| .then(res => { | |||||
| .then(res => { | |||||
| setScrappeDetails(res.data) | setScrappeDetails(res.data) | ||||
| if (res.data.status === 'done') | if (res.data.status === 'done') | ||||
| setScrappeResults(res.data.result) | setScrappeResults(res.data.result) | ||||
| else { | |||||
| interval = setInterval(() => { | |||||
| getByIdScrappe(id).then(res => { | |||||
| setScrappeDetails(res.data) | |||||
| if (res.data.status === 'done') { | |||||
| setScrappeResults(res.data.result) | |||||
| } | |||||
| }) | |||||
| }, 10000); | |||||
| } | |||||
| }) | }) | ||||
| return () => clearInterval(interval) | |||||
| }, [setScrappeResults]) | }, [setScrappeResults]) | ||||
| console.log("scrappeDetails", scrappeDetails) | |||||
| async function handleExecuteScrappes() { | |||||
| const res = await executeScrappes(scrappeDetails._id) | |||||
| if (res.status === 204) { | |||||
| getByIdScrappe(id) | |||||
| .then(res => { | |||||
| setScrappeDetails(res.data) | |||||
| }) | |||||
| } | |||||
| } | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| {console.log(id)} | |||||
| <nav className="main-header navbar navbar-expand-md navbar-light navbar-white"> | <nav className="main-header navbar navbar-expand-md navbar-light navbar-white"> | ||||
| <div className="container"> | <div className="container"> | ||||
| </nav> | </nav> | ||||
| <div className='content-wrapper'> | <div className='content-wrapper'> | ||||
| <div className='content-header'> | <div className='content-header'> | ||||
| <ScrappeDetails details={scrappeDetails} /> | |||||
| <ScrappeDetails handleExecuteScrappes={handleExecuteScrappes} details={scrappeDetails} /> | |||||
| </div> | </div> | ||||
| <div className='content mt-1' > | <div className='content mt-1' > | ||||
| <div className="container"> | |||||
| <div className="row"> | |||||
| {console.log(scrappeResults)} | |||||
| {(scrappeResults !== undefined) ? | |||||
| scrappeResults.map((result, i) => <ScrappeResult key={i} index={i} type={scrappeDetails.filters.find(el => el.name == "type" && el.value !== null)} result={result} />) | |||||
| : '' | |||||
| } | |||||
| <div className="container"> | |||||
| <div className="row"> | |||||
| {console.log(scrappeResults)} | |||||
| {(scrappeResults !== undefined) ? | |||||
| scrappeResults.map((result, i) => <ScrappeResult key={i} index={i} type={scrappeDetails.filters.find(el => el.name == "type" && el.value !== null)} result={result} />) | |||||
| : '' | |||||
| } | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; |
| getAll: 'scrapes', | getAll: 'scrapes', | ||||
| getById: 'scrapes/{id}', | getById: 'scrapes/{id}', | ||||
| create: 'scrapes/', | create: 'scrapes/', | ||||
| execute: 'scrapes/{id}/execute' | |||||
| execute: 'scrapes/{id}/execute', | |||||
| download: '/scrapes/{id}/files/xml', | |||||
| estimate: '/scrapes/estimate' | |||||
| }, | }, | ||||
| accounts: { | accounts: { | ||||
| get: 'accounts/{accountUid}', | get: 'accounts/{accountUid}', |
| export const getByIdScrappe = (id) => getRequest(replaceInUrl(apiEndpoints.scrappe.getById, { id })) | export const getByIdScrappe = (id) => getRequest(replaceInUrl(apiEndpoints.scrappe.getById, { id })) | ||||
| export const createScrappes = (scrappe) => postRequest(apiEndpoints.scrappe.create, scrappe) | export const createScrappes = (scrappe) => postRequest(apiEndpoints.scrappe.create, scrappe) | ||||
| export const executeScrappes = (id) => patchRequest(replaceInUrl(apiEndpoints.scrappe.execute, { id })) | export const executeScrappes = (id) => patchRequest(replaceInUrl(apiEndpoints.scrappe.execute, { id })) | ||||
| export const estimateScrappes = (scrape) => postRequest(apiEndpoints.scrappe.estimate, scrape) |