diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js index 2bd8a2f..597eb57 100644 --- a/electron/decoder/decoder.js +++ b/electron/decoder/decoder.js @@ -3,8 +3,37 @@ const path = require("path"); const _ = require("lodash"); const log = require("electron-log"); const { store } = require("../electron-store"); +const { BrowserWindow } = require("electron"); +const ipcTypes = require("../../src/ipc.types"); +const { + NewNotification, +} = require("../notification-wrapper/notification-wrapper"); -async function DecodeEstimate(filePath) { +async function ImportJob(path) { + const b = BrowserWindow.getAllWindows()[0]; + b.webContents.send(ipcTypes.default.estimate.toRenderer.estimateDecodeStart); + const newJob = await DecodeEstimate(path); + + if (newJob && !newJob.ERROR) { + b.webContents.send( + ipcTypes.default.estimate.toRenderer.estimateDecodeSuccess, + newJob + ); + log.info(`Sent job for upload. ${newJob.clm_no}`); + NewNotification({ + title: "Job Uploaded", + body: "A new job has been uploaded.", + }).show(); + } else { + log.info(`Ignored job. ${newJob.ERROR}`); + NewNotification({ + title: "Job Ignored", + body: newJob.ERROR, + }).show(); + } +} + +async function DecodeEstimate(filePath, includeFilePathInReturnJob = false) { const parsedFilePath = path.parse(filePath); let extensionlessFilePath = path.join( parsedFilePath.dir, @@ -15,6 +44,7 @@ async function DecodeEstimate(filePath) { ...(await DecodeVehFile(extensionlessFilePath)), ...(await DecodeTtlFile(extensionlessFilePath)), ...(await DecodeLinFile(extensionlessFilePath)), + ...(includeFilePathInReturnJob ? { filePath } : {}), }; const ad2 = await DecodeAd2File(extensionlessFilePath); @@ -297,12 +327,12 @@ async function DecodeLinFile(extensionlessFilePath) { !!jobline.act_price && jobline.act_price > 0 ) { - log.info( - "DB Price null/lower than act price", - jobline.line_desc, - jobline.db_price, - jobline.act_price - ); + // log.info( + // "DB Price null/lower than act price", + // jobline.line_desc, + // jobline.db_price, + // jobline.act_price + // ); jobline.db_price = jobline.act_price; } @@ -311,12 +341,12 @@ async function DecodeLinFile(extensionlessFilePath) { jobline.act_price && jobline.act_price > jobline.db_price ) { - log.info( - "Act price higher than existing db price", - jobline.line_desc, - jobline.db_price, - jobline.act_price - ); + // log.info( + // "Act price higher than existing db price", + // jobline.line_desc, + // jobline.db_price, + // jobline.act_price + // ); jobline.db_price = jobline.act_price; } @@ -344,3 +374,4 @@ async function DecodeLinFile(extensionlessFilePath) { } exports.DecodeEstimate = DecodeEstimate; +exports.ImportJob = ImportJob; diff --git a/electron/file-scan/file-scan-ipc.js b/electron/file-scan/file-scan-ipc.js new file mode 100644 index 0000000..4f5d84a --- /dev/null +++ b/electron/file-scan/file-scan-ipc.js @@ -0,0 +1,22 @@ +const { ipcMain } = require("electron"); +const ipcTypes = require("../../src/ipc.types"); +const { ImportJob } = require("../decoder/decoder"); +const { GetListOfEstimates } = require("./file-scan"); + +ipcMain.on( + ipcTypes.default.fileScan.toMain.scanFilePaths, + async (event, object) => { + const ret = await GetListOfEstimates(); + event.reply( + ipcTypes.default.fileScan.toRenderer.scanFilePathsResponse, + ret + ); + } +); + +ipcMain.on( + ipcTypes.default.fileScan.toMain.importJob, + async (event, filePath) => { + await ImportJob(filePath); + } +); diff --git a/electron/file-scan/file-scan.js b/electron/file-scan/file-scan.js new file mode 100644 index 0000000..01e65e1 --- /dev/null +++ b/electron/file-scan/file-scan.js @@ -0,0 +1,42 @@ +const path = require("path"); +const fs = require("fs"); +const { store } = require("../electron-store"); +const log = require("electron-log"); +const fsPromises = fs.promises; +const _ = require("lodash"); +const { DecodeEstimate } = require("../decoder/decoder"); + +async function GetListOfEstimates() { + const ListOfEnvFiles = await GetEnvFiles(); + const ListOfSummarizedEstimates = await ReadAllEstimates(ListOfEnvFiles); + const FilteredListOfSummarizedEstimates = ListOfSummarizedEstimates.filter( + (j) => !j.ERROR + ); + return FilteredListOfSummarizedEstimates; +} + +async function ReadAllEstimates(ListOfEnvFiles) { + return await Promise.all( + ListOfEnvFiles.map(async (e) => await DecodeEstimate(e, true)) + ); +} + +async function GetEnvFiles() { + const filePaths = store.get("filePaths"); + const allFilePaths = []; + await Promise.all( + filePaths.map(async (fp) => { + const envFilesInDir = (await fsPromises.readdir(fp)).filter((p) => + p.toUpperCase().includes(".ENV") + ); + + envFilesInDir.map((envFileName) => { + allFilePaths.push(path.join(fp, envFileName)); + return null; + }); + }) + ); + return allFilePaths; +} + +exports.GetListOfEstimates = GetListOfEstimates; diff --git a/electron/file-watcher/file-watcher.js b/electron/file-watcher/file-watcher.js index 9331c3d..ff7866d 100644 --- a/electron/file-watcher/file-watcher.js +++ b/electron/file-watcher/file-watcher.js @@ -1,7 +1,7 @@ const chokidar = require("chokidar"); const ipcTypes = require("../../src/ipc.types"); const path = require("path"); -const { DecodeEstimate } = require("../decoder/decoder"); +const { ImportJob } = require("../decoder/decoder"); const { BrowserWindow } = require("electron"); const { store } = require("../electron-store"); const { @@ -36,16 +36,7 @@ async function StartWatcher() { watcher = chokidar.watch(filePaths, { ignored: (fp, stats) => { const p = path.parse(fp); - log.log( - "Checking if should ignore.", - fp, - p, - p.ext !== "" && p.ext !== ".ENV" && p.ext !== ".env" - ); - // prettier-ignore - // const ignore = RegExp("^.*(?", fp, "Ignore?", ignore); - return (p.ext !== "" && p.ext !== ".ENV" && p.ext !== ".env" ); + return p.ext !== "" && p.ext.toUpperCase() !== ".ENV"; }, usePolling: store.get("polling").enabled || false, interval: store.get("polling").pollingInterval || 1000, @@ -116,25 +107,5 @@ exports.StopWatcher = StopWatcher; exports.watcher = watcher; async function HandleNewFile(path) { - const b = BrowserWindow.getAllWindows()[0]; - b.webContents.send(ipcTypes.default.estimate.toRenderer.estimateDecodeStart); - const newJob = await DecodeEstimate(path); - - if (newJob && !newJob.ERROR) { - b.webContents.send( - ipcTypes.default.estimate.toRenderer.estimateDecodeSuccess, - newJob - ); - log.info(`Sent job for upload. ${newJob.clm_no}`); - NewNotification({ - title: "Job Uploaded", - body: "A new job has been uploaded.", - }).show(); - } else { - log.info(`Ignored job. ${newJob.ERROR}`); - NewNotification({ - title: "Job Ignored", - body: newJob.ERROR, - }).show(); - } + await ImportJob(path); } diff --git a/electron/ipc-main-handler.js b/electron/ipc-main-handler.js index 2fa6e25..54f9858 100644 --- a/electron/ipc-main-handler.js +++ b/electron/ipc-main-handler.js @@ -1,16 +1,14 @@ const { ipcMain } = require("electron"); const { default: ipcTypes } = require("../src/ipc.types"); const { store } = require("./electron-store"); -const { watcher } = require("./file-watcher/file-watcher"); - //Import Ipc Handlers require("./file-watcher/file-watcher-ipc"); +require("./file-scan/file-scan-ipc"); console.log("*** Added IPC Handlers ***"); ipcMain.on("test", async (event, object) => { console.log("Received test IPC Command"); - //const job = await DecodeEstimate("C:\\VPS\\EMS\\687_3_A.AD1"); event.reply("test-toRenderer", { status: 0, message: null }); }); diff --git a/src/App/App.styles.scss b/src/App/App.styles.scss index 7a09eba..e7a0b20 100644 --- a/src/App/App.styles.scss +++ b/src/App/App.styles.scss @@ -3,6 +3,20 @@ body { overflow: hidden; } +.imex-table-header { + display: flex; + flex-wrap: wrap; + justify-content: center; + + &__search { + flex: 1; + } + & > * { + margin-left: 0.5rem; + margin-right: 0.5rem; + } +} + .imex-flex-row { display: flex; justify-content: flex-start; @@ -60,8 +74,6 @@ body { height: 100%; } - - //Required for the tab with infinite loading .ant-tabs-content { height: 100%; diff --git a/src/components/atoms/last-scanned/last-scanned.atom.jsx b/src/components/atoms/last-scanned/last-scanned.atom.jsx new file mode 100644 index 0000000..61e7457 --- /dev/null +++ b/src/components/atoms/last-scanned/last-scanned.atom.jsx @@ -0,0 +1,23 @@ +import { Typography } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectScanLastScanned } from "../../../redux/scan/scan.selectors"; +import TimeAgoFormatter from "../../atoms/time-ago-formatter/time-ago-formatter.atom"; + +const mapStateToProps = createStructuredSelector({ + lastScanned: selectScanLastScanned, +}); +const mapDispatchToProps = (dispatch) => ({}); + +export function LastScannedAtom({ lastScanned }) { + return ( + lastScanned && ( + + Last scanned + {lastScanned} + + ) + ); +} +export default connect(mapStateToProps, mapDispatchToProps)(LastScannedAtom); diff --git a/src/components/atoms/scan-refresh/scan-refresh.atom.jsx b/src/components/atoms/scan-refresh/scan-refresh.atom.jsx new file mode 100644 index 0000000..b43ff03 --- /dev/null +++ b/src/components/atoms/scan-refresh/scan-refresh.atom.jsx @@ -0,0 +1,22 @@ +import { Button } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { scanStart } from "../../../redux/scan/scan.actions"; +import { selectScanLoading } from "../../../redux/scan/scan.selectors"; + +const mapStateToProps = createStructuredSelector({ + scanLoading: selectScanLoading, +}); +const mapDispatchToProps = (dispatch) => ({ + scanStart: () => dispatch(scanStart()), +}); + +export function ScanRefreshAtom({ scanLoading, scanStart }) { + return ( + + ); +} +export default connect(mapStateToProps, mapDispatchToProps)(ScanRefreshAtom); diff --git a/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx b/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx index 87e874b..d7d5217 100644 --- a/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx +++ b/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx @@ -1,12 +1,19 @@ import { Tooltip } from "antd"; import moment from "moment"; -import React from "react"; +import React, { useEffect, useState } from "react"; export default function TimeAgoFormatter(props) { + const [timestampString, setTimestampString] = useState(""); const m = moment(props.children); + useEffect(() => { + const timer = setInterval(() => setTimestampString(m.fromNow()), 15000); + setTimestampString(m.fromNow()); + return () => clearInterval(timer); + }, [m]); + return props.children ? ( - {m.fromNow()} + {timestampString} ) : null; } diff --git a/src/components/molecules/reporting-jobs-list/reporting-jobs-list.molecule.jsx b/src/components/molecules/reporting-jobs-list/reporting-jobs-list.molecule.jsx index 9e3fa8c..16fa7ca 100644 --- a/src/components/molecules/reporting-jobs-list/reporting-jobs-list.molecule.jsx +++ b/src/components/molecules/reporting-jobs-list/reporting-jobs-list.molecule.jsx @@ -1,13 +1,13 @@ import { Input, Table } from "antd"; import React, { useState } from "react"; import { connect } from "react-redux"; +import { Link } from "react-router-dom"; import { createStructuredSelector } from "reselect"; +import { setSelectedJobId } from "../../../redux/application/application.actions"; import { selectReportData, selectReportLoading, } from "../../../redux/reporting/reporting.selectors"; -import { setSelectedJobId } from "../../../redux/application/application.actions"; -import { Link } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ reportingLoading: selectReportLoading, reportData: selectReportData, @@ -108,9 +108,11 @@ export function ReportingJobsListMolecule({ searchText !== "" ? reportData.filter( (j) => + j.v_makedesc.toLowerCase().includes(searchText.toLowerCase()) || + j.v_model.toLowerCase().includes(searchText.toLowerCase()) || j.ownr_fn.toLowerCase().includes(searchText.toLowerCase()) || j.ownr_ln.toLowerCase().includes(searchText.toLowerCase()) || - j.ownr_clm_no.toLowerCase().includes(searchText.toLowerCase()) + j.clm_no.toLowerCase().includes(searchText.toLowerCase()) ) : reportData; diff --git a/src/components/molecules/scan-estimate-list/scan-estimate-list.molecule.jsx b/src/components/molecules/scan-estimate-list/scan-estimate-list.molecule.jsx new file mode 100644 index 0000000..f86f68e --- /dev/null +++ b/src/components/molecules/scan-estimate-list/scan-estimate-list.molecule.jsx @@ -0,0 +1,116 @@ +import { Button, Input, Table } from "antd"; +import React, { useState } from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import ipcTypes from "../../../ipc.types"; +import { + selectScanEstimates, + selectScanLoading, +} from "../../../redux/scan/scan.selectors"; +import LastScannedAtom from "../../atoms/last-scanned/last-scanned.atom"; +import ScanRefreshAtom from "../../atoms/scan-refresh/scan-refresh.atom"; + +const { ipcRenderer } = window; +const mapStateToProps = createStructuredSelector({ + scanLoading: selectScanLoading, + estimates: selectScanEstimates, +}); +const mapDispatchToProps = (dispatch) => ({}); + +export function ScanEstimateListMolecule({ scanLoading, estimates }) { + const [searchText, setSearchText] = useState(""); + + const columns = [ + { + title: "Claim No.", + dataIndex: "clm_no", + key: "clm_no", + }, + { + title: "Ins Co.", + dataIndex: "ins_co_nm", + key: "ins_co_nm", + }, + { + title: "First Name", + dataIndex: "ownr_fn", + key: "ownr_fn", + }, + { + title: "Last Name", + dataIndex: "ownr_ln", + key: "ownr_ln", + }, + { + title: "Vehicle", + dataIndex: "vehicle", + key: "vehicle", + render: (text, record) => + `${record.v_model_yr} ${record.v_makedesc} ${record.v_model} (${record.v_type})`, + }, + { + title: "Import", + dataIndex: "import", + key: "import", + render: (text, record) => ( + + ), + }, + ]; + + const data = + searchText !== "" + ? estimates.filter( + (j) => + j.v_makedesc.toLowerCase().includes(searchText.toLowerCase()) || + j.v_model.toLowerCase().includes(searchText.toLowerCase()) || + j.ownr_fn.toLowerCase().includes(searchText.toLowerCase()) || + j.ownr_ln.toLowerCase().includes(searchText.toLowerCase()) || + j.clm_no.toLowerCase().includes(searchText.toLowerCase()) + ) + : estimates; + + return ( +
+ ( +
+ + + { + setSearchText(val); + }} + enterButton + allowClear + /> +
+ )} + columns={columns} + rowKey="filepath" + loading={scanLoading} + size="small" + pagination={false} + dataSource={data} + scroll={{ + x: true, + }} + /> + + ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(ScanEstimateListMolecule); diff --git a/src/components/organisms/sider-menu/sider-menu.organism.jsx b/src/components/organisms/sider-menu/sider-menu.organism.jsx index e82410e..1bd3ef1 100644 --- a/src/components/organisms/sider-menu/sider-menu.organism.jsx +++ b/src/components/organisms/sider-menu/sider-menu.organism.jsx @@ -3,6 +3,7 @@ import { SettingFilled, CloseOutlined, BarChartOutlined, + FileAddFilled, } from "@ant-design/icons"; import { Menu } from "antd"; import React from "react"; @@ -20,6 +21,9 @@ export default function SiderMenuOrganism() { }> Jobs + }> + File Scan + }> Reporting diff --git a/src/components/pages/routes/routes.page.jsx b/src/components/pages/routes/routes.page.jsx index 1f523b6..94a5c5b 100644 --- a/src/components/pages/routes/routes.page.jsx +++ b/src/components/pages/routes/routes.page.jsx @@ -8,6 +8,7 @@ import ErrorResultAtom from "../../atoms/error-result/error-result.atom"; import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism"; import JobsPage from "../jobs/jobs.page"; import ReportingPage from "../reporting/reporting.page"; +import ScanPage from "../scan/scan.page"; import SettingsPage from "../settings/settings.page"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop }); @@ -35,6 +36,7 @@ export function RoutesPage({ bodyshop }) { + diff --git a/src/components/pages/scan/scan.page.jsx b/src/components/pages/scan/scan.page.jsx new file mode 100644 index 0000000..60eeb46 --- /dev/null +++ b/src/components/pages/scan/scan.page.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import ScanEstimateListMolecule from "../../molecules/scan-estimate-list/scan-estimate-list.molecule"; + +export default function ScanPage() { + return ( +
+ +
+ ); +} diff --git a/src/ipc.types.js b/src/ipc.types.js index 27ceace..6ee0fa0 100644 --- a/src/ipc.types.js +++ b/src/ipc.types.js @@ -15,6 +15,15 @@ exports.default = { set: "store_set", response: "store_response", }, + fileScan: { + toMain: { + scanFilePaths: "fileScan__scanFilePaths", + importJob: "fileScan__importJob", + }, + toRenderer: { + scanFilePathsResponse: "fileScan__scanFilePathsResponse", + }, + }, fileWatcher: { toMain: { filepathsGet: "filewatcher__filepathsget", diff --git a/src/ipc/ipc-estimate-utils.js b/src/ipc/ipc-estimate-utils.js index 8b684e9..0de15fc 100644 --- a/src/ipc/ipc-estimate-utils.js +++ b/src/ipc/ipc-estimate-utils.js @@ -1,5 +1,6 @@ import gql from "graphql-tag"; import _ from "lodash"; +import moment from "moment"; import client from "../graphql/GraphQLClient"; import { INSERT_NEW_JOB, @@ -8,7 +9,6 @@ import { } from "../graphql/jobs.queries"; import { QUERY_GROUPS_BY_MAKE_TYPE } from "../graphql/veh_group.queries"; import { store } from "../redux/store"; -import moment from "moment"; const { logger } = window; export async function UpsertEstimate(job) { @@ -76,8 +76,8 @@ export async function UpsertEstimate(job) { export const GetSupplementDelta = async (jobId, existingLinesO, newLines) => { const existingLines = _.cloneDeep(existingLinesO); - console.log("GetSupplementDelta -> newLines", newLines); - console.log("GetSupplementDelta -> existingLines", existingLines); + //console.log("GetSupplementDelta -> newLines", newLines); + //console.log("GetSupplementDelta -> existingLines", existingLines); const linesToInsert = []; const linesToUpdate = []; diff --git a/src/ipc/ipc-renderer-handler.js b/src/ipc/ipc-renderer-handler.js index 6fecb22..b14dbdf 100644 --- a/src/ipc/ipc-renderer-handler.js +++ b/src/ipc/ipc-renderer-handler.js @@ -6,7 +6,7 @@ import { } from "../redux/application/application.actions"; import { store } from "../redux/store"; import { UpsertEstimate } from "./ipc-estimate-utils"; - +import { setScanEstimateList } from "../redux/scan/scan.actions"; const { ipcRenderer } = window; console.log("----Initializing IPC Listeners in React App."); @@ -57,3 +57,11 @@ ipcRenderer.on( ipcRenderer.on(ipcTypes.default.store.response, (event, obj) => { store.dispatch(setSettings(obj)); }); + +//FileScan Section +ipcRenderer.on( + ipcTypes.default.fileScan.toRenderer.scanFilePathsResponse, + async (event, listOfEstimates) => { + store.dispatch(setScanEstimateList(listOfEstimates)); + } +); diff --git a/src/redux/root.reducer.js b/src/redux/root.reducer.js index eaeda26..0a39bd0 100644 --- a/src/redux/root.reducer.js +++ b/src/redux/root.reducer.js @@ -4,17 +4,19 @@ import storage from "redux-persist/lib/storage"; import applicationReducer from "./application/application.reducer"; import userReducer from "./user/user.reducer"; import reportingReducer from "./reporting/reporting.reducer"; +import scanReducer from './scan/scan.reducer' const persistConfig = { key: "root", storage, - blacklist: ["application", "user", "reporting"], + blacklist: ["application", "user", "reporting", "scan"], }; const rootReducer = combineReducers({ application: applicationReducer, user: userReducer, reporting: reportingReducer, + scan: scanReducer }); export default persistReducer(persistConfig, rootReducer); diff --git a/src/redux/root.saga.js b/src/redux/root.saga.js index d348a07..5ce2551 100644 --- a/src/redux/root.saga.js +++ b/src/redux/root.saga.js @@ -2,6 +2,12 @@ import { all, call } from "redux-saga/effects"; import { applicationSagas } from "./application/application.sagas"; import { userSagas } from "./user/user.sagas"; import { reportingSagas } from "./reporting/reporting.sagas"; +import { scanSagas } from "./scan/scan.sagas"; export default function* rootSaga() { - yield all([call(applicationSagas), call(userSagas), call(reportingSagas)]); + yield all([ + call(applicationSagas), + call(userSagas), + call(reportingSagas), + call(scanSagas), + ]); } diff --git a/src/redux/scan/scan.actions.js b/src/redux/scan/scan.actions.js new file mode 100644 index 0000000..5be37cd --- /dev/null +++ b/src/redux/scan/scan.actions.js @@ -0,0 +1,18 @@ +import ScanActionTypes from "./scan.types"; + +export const setScanLoading = () => ({ + type: ScanActionTypes.SET_SCAN_LOADING, +}); + +export const setScanEstimateList = (listOfEstimates) => ({ + type: ScanActionTypes.SET_LIST_OF_ESTIMATES, + payload: listOfEstimates, +}); + +export const clearScanEstimateList = () => ({ + type: ScanActionTypes.CLEAR_LIST_OF_ESTIMATES, +}); + +export const scanStart = () => ({ + type: ScanActionTypes.SCAN_START, +}); diff --git a/src/redux/scan/scan.reducer.js b/src/redux/scan/scan.reducer.js new file mode 100644 index 0000000..ffd5948 --- /dev/null +++ b/src/redux/scan/scan.reducer.js @@ -0,0 +1,27 @@ +import ScanActionTypes from "./scan.types"; + +const INITIAL_STATE = { + loading: false, + lastScanned: null, + estimates: [], +}; + +const applicationReducer = (state = INITIAL_STATE, action) => { + switch (action.type) { + case ScanActionTypes.SET_LIST_OF_ESTIMATES: + return { + ...state, + loading: false, + estimates: action.payload, + lastScanned: new Date(), + }; + case ScanActionTypes.SCAN_START: + return { ...state, loading: true }; + case ScanActionTypes.CLEAR_LIST_OF_ESTIMATES: + return { ...state, estimates: [], lastScanned: null }; + default: + return state; + } +}; + +export default applicationReducer; diff --git a/src/redux/scan/scan.sagas.js b/src/redux/scan/scan.sagas.js new file mode 100644 index 0000000..4a0a134 --- /dev/null +++ b/src/redux/scan/scan.sagas.js @@ -0,0 +1,18 @@ +import { all, call, takeLatest } from "redux-saga/effects"; +import ipcTypes from "../../ipc.types"; +import ScanActionTypes from "./scan.types"; + +const { ipcRenderer } = window; + +export function* onScanStart() { + yield takeLatest(ScanActionTypes.SCAN_START, handleScanStart); +} + +// eslint-disable-next-line require-yield +export function* handleScanStart() { + ipcRenderer.send(ipcTypes.default.fileScan.toMain.scanFilePaths); +} + +export function* scanSagas() { + yield all([call(onScanStart)]); +} diff --git a/src/redux/scan/scan.selectors.js b/src/redux/scan/scan.selectors.js new file mode 100644 index 0000000..ed119fb --- /dev/null +++ b/src/redux/scan/scan.selectors.js @@ -0,0 +1,17 @@ +import { createSelector } from "reselect"; + +const selectScan = (state) => state.scan; + +export const selectScanLoading = createSelector( + [selectScan], + (scan) => scan.loading +); +export const selectScanEstimates = createSelector( + [selectScan], + (scan) => scan.estimates +); + +export const selectScanLastScanned = createSelector( + [selectScan], + (scan) => scan.lastScanned +); diff --git a/src/redux/scan/scan.types.js b/src/redux/scan/scan.types.js new file mode 100644 index 0000000..6f8c4f3 --- /dev/null +++ b/src/redux/scan/scan.types.js @@ -0,0 +1,7 @@ +const ScanActionTypes = { + SET_LIST_OF_ESTIMATES: "SET_LIST_OF_ESTIMATES", + SET_SCAN_LOADING: "SET_SCAN_LOADING", + CLEAR_LIST_OF_ESTIMATES: "CLEAR_LIST_OF_ESTIMATES", + SCAN_START: "SCAN_START", +}; +export default ScanActionTypes;