DEV Community

Cover image for Watchlist application using ReactJS, NodeJS
Sandeep
Sandeep

Posted on

Watchlist application using ReactJS, NodeJS

today we create watchlist application using reactjs, nodejs.

Backend

config.js

{
    "DB_URL": "mongodb+srv://user:password@cluster0.wb0iyao.mongodb.net/?retryWrites=true&w=majority"
}
Enter fullscreen mode Exit fullscreen mode

default.js

module.exports.defaultSymbols=[
    {
        symbol:"SYY",
        bid:"sp",
        ask:"sp",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"KJJ",
        bid:"d",
        ask:"d",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"LLL",
        bid:"ru",
        ask:"ru",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"NHD",
        bid:"nd",
        ask:"nd",
        bidSize:2,
        askSize:6,
        volume:0,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"QWA",
        bid:"ts",
        ask:"ts",
        bidSize:7,
        askSize:4,
        volume:1,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    }
]
Enter fullscreen mode Exit fullscreen mode

watchlist.controller.js

const WatchListModel = require("../models/WatchList")

module.exports.getWatchLists=async(req,res,next)=>{
    try {
        const watchLists=await WatchListModel.find({});
        res.status(200).send(watchLists);
    } catch (error) {
        next(error)
    }
}

module.exports.addSymbol=async(req,res,next)=>{
    try {
        const {symbol,bid,ask,bidSize,askSize,volume}=req.body;
        const symbolObj={
            symbol,
            bid,
            ask,
            bidSize,
            askSize,
            volume
        }
        const watchList=await WatchListModel.findOneAndUpdate({
            _id: req.params.id
        },{
          $push:{
            symbols:symbolObj
          }
        },{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}

module.exports.updateSymbol=async(req,res,next)=>{
    try {
        const {symbolId,symbol,bid,ask,bidSize,askSize,volume,description}=req.body;
        const symbolObj={
            symbol,
            bid,
            ask,
            bidSize,
            askSize,
            volume,description
        }
        let watchList=await WatchListModel.findOne({
            _id: req.params.id
        })
        const index=watchList?.symbols.findIndex((symbol)=>symbol?._id?.toString()==symbolId);
        watchList.symbols[index]=symbolObj;
        watchList=await WatchListModel.findOneAndUpdate({_id: req.params.id},{$set:{
            symbols:watchList?.symbols
        }},{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}

module.exports.removeSymbol=async(req,res,next)=>{
    try {
        const {symbolId}=req.body
        let watchList=await WatchListModel.findOne({
            _id: req.params.id
        })
        const updatedSymbols=watchList?.symbols.filter((symbol)=>symbol?._id?.toString()!==symbolId);
        watchList=await WatchListModel.findOneAndUpdate({_id: req.params.id},{$set:{
            symbols:updatedSymbols
        }},{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}


Enter fullscreen mode Exit fullscreen mode

createDefaultwatchlist.middleware.js

const WatchListModel = require("../models/WatchList")
const symbols=require('../constants/defaults')

module.exports.createDefaultWatchList=async(req,res,next)=>{
    try {
        let watchList=await WatchListModel.findOne({});
        console.log(watchList)
        if(!watchList){
            watchList=new WatchListModel({symbols:symbols.defaultSymbols});
            watchList=await watchList.save();
            next(watchList)
        }
    } catch (error) {
        next(error)
    }
}
Enter fullscreen mode Exit fullscreen mode

error.middleware.js


module.exports=async(err,req,res)=>{
    try {
        if(err){
            return res.status(500).send({status:500,message:err})
        }
    } catch (error) {
        console.log(error.body);
    }
}
Enter fullscreen mode Exit fullscreen mode

validateObjectId.middleware.js

const mongoose= require("mongoose")

module.exports.validateObjectId=(req,res,next)=>{
    try {
        if(!mongoose.Types.ObjectId.isValid(req.params.id))
            return res.status(400).send({message:"Invalid Id",status:400});
        next(); 
    } catch (error) {
        next(error)
    }
}
Enter fullscreen mode Exit fullscreen mode

watchlist.model.js

const mongoose=require('mongoose');

const {Schema,model}=mongoose;

const watchlistSchema=new Schema({
    symbols:[
        {
            symbol:{
                type:String,
                required: true,
                index:true
            },
            bid:{
                type:String,
                default:''
            },
            ask:{
                type:String,
                default:''
            },
            bidSize:{
                type:Number,
                default:null
            },
            askSize:{
                type:Number,
                default:null
            },
            volume:{
                type:Number,
                default:null
            },
            description:{
                type:String,
                default: ""
            }
        }
    ]
})

const WatchListModel=model('watchlist',watchlistSchema);

module.exports=WatchListModel;
Enter fullscreen mode Exit fullscreen mode

watchlist.route.js

const express=require('express');
const { createDefaultWatchList } = require("../middlewares/createDefaultWatchList.middleware");
const WatchListModel = require("../models/WatchList.model");
const router=express.Router();
const symbols=require('../constants/defaults');
const { addSymbol, getWatchLists, removeSymbol, updateSymbol } = require("../controllers/watchlist.controller");
const { validateObjectId } = require('../middlewares/validateObjectId.middleware');

router.get('',getWatchLists);

router.post('/create-default',createDefaultWatchList);

router.post('/add-symbol/:id',validateObjectId,addSymbol);

router.patch('/update-symbol/:id',validateObjectId,updateSymbol);

router.patch('/remove-symbol/:id',validateObjectId,removeSymbol);

module.exports=router;


(async ()=>{
    let watchList=await WatchListModel.findOne({});
    if(!watchList){
        watchList=new WatchListModel({symbols:symbols.defaultSymbols});
        watchList=await watchList.save();
        console.log('default watch list created')
    }
})()
Enter fullscreen mode Exit fullscreen mode

connection.js

const mongoose=require('mongoose');
const config=require('config');

const DB_URL=config.get('DB_URL');

module.exports=mongoose.connect(DB_URL).then((conn)=>{
    console.log(`Database connected on mongo server ${DB_URL}`);   
}).catch((err)=>{
    console.log(`Database Error ->:  ${err}`);  
})
Enter fullscreen mode Exit fullscreen mode

app.js

const express=require('express');
const cors=require('cors');

require('./services/connection');
const errorMiddleware=require('./middlewares/error.middleware');
const watchListRouter=require('./routes/watchlist.route')

const app=express();
const PORT=process.env.PORT|| 8000;
//middlewares
app.use(express.json());
app.use(express.urlencoded({extended:false}));
app.use(cors());
//routes of app
app.use('/api/v1/watchlist',watchListRouter);
//global error middleware 

app.use(errorMiddleware)

app.listen(PORT,()=>{
    console.log(`server is running on the port ${PORT}`)
})

Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "Sandeep Jaiswal",
  "license": "MIT",
  "dependencies": {
    "config": "^3.3.9",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "mongoose": "^7.0.3"
  }
}

Enter fullscreen mode Exit fullscreen mode

Frontend

Header.js

import React from 'react'

const Header = () => {
    return (
        <div className="container-fluid bg-primary">
            <nav className="navbar navbar-expand-lg bg-body-tertiary">
                <div className="container-fluid">
                    <a className="navbar-brand text-white fw-bold" href="#">Watchlist</a>
                </div>
            </nav>
        </div>
    )
}

export default Header
Enter fullscreen mode Exit fullscreen mode

UpdateWatch.js

import React, { useEffect, useState } from 'react'

const UpdateWatch = ({ data, updateSymbol }) => {
  const [form, setForm] = useState({
    symbolId: '', symbol: '', bid: '', ask: '', bidSize: null, askSize: null, volume: null, description: ''
  })
  useEffect(() => {
    setForm({ symbolId: data?._id, symbol: data?.symbol,
       bid: data?.bid, ask: data?.ask, bidSize: data?.bidSize||0,
        askSize: data?.askSize||0 ,volume:data?.volume||0,description:data?.description});
  }, [data?._id])

  const change = (event) => {
    const { name, value } = event.target;
    setForm({ ...form, [name]: value })
  }

  const submit = () => {
    updateSymbol(form);
    setForm({
      symbolId: '', symbol: '', bid: '', ask: '', bidSize: 0, askSize: 0, volume: 0, description: ''
    })
  }
  return (
    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h1 class="modal-title fs-5" id="exampleModalLabel">Update Symbol</h1>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            <form>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Symbol:</label>
                <input type="text" name="symbol" value={form.symbol} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Bid:</label>
                <input type="text" name="bid" value={form.bid} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">ask:</label>
                <input type="text" name="ask" value={form.ask} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Bid Size:</label>
                <input type="number" name="bidSize" value={form.bidSize} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Ask Size:</label>
                <input type="number" name="askSize" value={form.askSize} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Volume:</label>
                <input type="number" name="volume" value={form.volume} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Description:</label>
                <input type="text" name="description" value={form.description} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary" data-bs-dismiss="modal" onClick={() => submit()}> Update</button>
          </div>
        </div>
      </div>
    </div>
  )
}

export default UpdateWatch
Enter fullscreen mode Exit fullscreen mode

watchlist.js

import React, { useContext, useState } from 'react'
import WatchListContext from "../contexts/WatchListContext";
import { addSymbols, removeSymbols, updateSymbols } from "../services/watchList.service";
import UpdateWatch from "./common/UpdateWatch";

const WatchList = () => {
    const [watchlist, setWatchlist] = useContext(WatchListContext);
    const [updateObject,setUpdateObject]=useState({});
    const [selectedSymbol,setSelectedSymbol]=useState([])
    const addSymbol = async (event) => {
        if (event.keyCode === 13) {
            const res = await addSymbols(watchlist?._id, { symbol: event.target.value });
            setWatchlist(res.data);
            event.target.value = '';
        }
    }

    const removeSymbol = async (symbolId) => {
            const res = await removeSymbols(watchlist?._id, { symbolId });
            setWatchlist(res.data);
    }

    const updateSymbol = async (data) => {
        const res = await updateSymbols(watchlist?._id, data)
        setWatchlist(res.data);
    }
    const toggleSelect=(symbol)=>{
        const check=selectedSymbol.find(s=>s?._id===symbol?._id)
        if(check){
            let s=selectedSymbol.filter(sym=>sym?._id!==symbol?._id)
            setSelectedSymbol(s);
        }else{
            let s=[...selectedSymbol,symbol]
            setSelectedSymbol(s);
        }
    }
    return (
        <div>
            <div className="p-4">
                <div className="row">
                    <div className="col-3 fs-2">
                        Quote Details
                    </div>
                    <div className="col-9">
                        {selectedSymbol?.length ? 
                            selectedSymbol?.map((s)=>(
                                <div key={s?._id}>
                                    <h5>{s?.symbol}</h5>
                                    <p>{s?.description}</p>
                                </div>
                            )
                        ):"No Any row selected"}
                    </div>
                </div>
                <div className="border">
                    <div className="bg-light p-2 fw-bold">My Watchlist</div>
                    <table className="table table-bordered">
                        <thead>
                            <tr>
                                <th scope="col"></th>
                                <th scope="col">Bid</th>
                                <th scope="col">Ask</th>
                                <th scope="col">Bid Size</th>
                                <th scope="col">Ask Size</th>
                                <th scope="col">Volume</th>
                                <th scope="col">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                        {watchlist?.symbols.map((symbol) => (
                        <tr key={symbol?._id} onClick={()=>toggleSelect(symbol)} className={selectedSymbol?.find(s=>s?._id===symbol?._id)?'table-success':''}>
                        <th scope="row">{symbol?.symbol}</th>
                        <td>{symbol?.bid}</td>
                        <td>{symbol?.ask}</td>
                        <td>{symbol?.bidSize}</td>
                        <td>{symbol?.askSize}</td>
                        <td>{symbol?.volume}</td>
                        <td>
                            <div className="d-flex justify-content-around">
                            <button className="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@mdo" onClick={()=>setUpdateObject(symbol)}>Edit</button>
                            <button className="btn btn-danger" onClick={()=>removeSymbol(symbol?._id)}>Delete</button>
                            </div></td>
                    </tr>    
                    ))}

                        </tbody>
                    </table>
                    <div className="bg-light p-1 w-100" style={{ height: 40 }}>
                        Add Symbol <input className="ms-2 rounded" placeholder="" onKeyUp={(e) => addSymbol(e)} />
                    </div>
                </div>
                <UpdateWatch data={updateObject} updateSymbol={updateSymbol}/>
            </div>
        </div>
    )
}

export default WatchList
Enter fullscreen mode Exit fullscreen mode

watchlist.service.js

import { toast } from "react-toastify";

const { http, URL } = require("./http.service")


export const getWatchList = async () => {
    try {
        const res = await http.get(URL + '/watchlist');
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const addSymbols = async (watchListId,payload) => {
    try {
        const res = await http.post(URL + '/watchlist/add-symbol/'+watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const removeSymbols = async (watchListId, payload) => {
    try {
        const res = await http.patch(URL +'/watchlist/remove-symbol/' + watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const updateSymbols = async (watchListId, payload) => {
    try {
        const res = await http.patch(URL +'/watchlist/update-symbol/' + watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const isLogged = () => {
    const token = localStorage.getItem('token');
    return token ? true : false;
}

export const logoutUser = () => {
    localStorage.removeItem('token');
}

export const getToken = () => {
    return localStorage.getItem('token');
}

export const handleError = (error) => {
    toast.error(error?.message)
}

export const setCredentials=(token)=>{
    http.defaults.headers.common['x-auth-token'] = token;
}
Enter fullscreen mode Exit fullscreen mode

App.js

import { useEffect, useState } from "react";
import './App.css';
import WatchList from "./components/WatchList";
import WatchListContext from "./contexts/WatchListContext";
import { getWatchList } from "./services/watchList.service";
import Header from "./components/common/Header";
import Loader from "./components/common/Loader";

function App() {
  const [watchlist,setWatchlist]=useState();
  const [isLoading,setIsLoading]=useState(false);

  useEffect(()=>{
    getWatchListData()
  },[]);

  const getWatchListData=async ()=>{
    setIsLoading(true)
    const result=await getWatchList();
    if(result.status ===200 ){
      setWatchlist(result?.data?.[0])
      setIsLoading(false)
    }else{
      setIsLoading(false)
    }
  }

  return (
    <WatchListContext.Provider value={[watchlist,setWatchlist]}>
         <Header />
         {isLoading?<Loader />:<WatchList />}
    </WatchListContext.Provider>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

this is not of the complete code if you have any doubt regarding this comment in this post. I give the answer as soon as possible. thank you for reading my post.

Top comments (0)